Skip to content
IgorCSISPublic

About

Animate a still image with Wan 2.2. Free, open source, runs in your browser.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

Repository files navigation

NiftyVid: image to video with Wan 2.2

NiftyVid

Animate a still image with Wan 2.2 in your browser. Free. Open source.

Live demo · Portfolio · Report an issue

Astro Tailwind Cloudflare Workers Hugging Face License

What it does

Drop a photo, write a short motion prompt ("slow camera push, leaves drifting"), and a few seconds later you have a short MP4 of that image animated. Under the hood, NiftyVid sends the image and prompt to a public Hugging Face Space running Wan 2.2 image-to-video, the same model class as Runway Gen-3 and Kling, but open source and free to use on a shared GPU.

The site itself is static and hosted on GitHub Pages, so there's nothing to install or sign up for.

Features

  • Drag-and-drop or paste an image straight from the clipboard
  • Live prompt with sensible defaults (duration, inference steps)
  • In-session history strip with hover-to-preview
  • Dark mode interface tuned for long sessions
  • Download generated videos as MP4
  • Zero tracking, no signup, no API keys to manage
  • Friendly error messages that surface what the upstream actually said, rather than a generic failure

Tech stack

Layer Choice
Frontend Astro 4 + Tailwind 3 + TypeScript
Hosting GitHub Pages (static)
Edge proxy Cloudflare Worker (free tier)
Inference Wan 2.2 image-to-video, on a public Hugging Face Space
Tooling pnpm 9, Wrangler 3, GitHub Actions

Architecture

[ browser ]  →  [ Cloudflare Worker ]  →  [ HF Space: Wan 2.2 ]
   GH Pages       nifty-vid.workers.dev      public, free GPU

The Worker exists for two reasons. Browsers can't reliably call Hugging Face Spaces because of CORS, and we want one stable URL we control so the upstream backend can change without redeploying the static site. Today the Worker proxies to a public Space; tomorrow it could swap to fal.ai, Replicate, or a private Space without touching the frontend.

The handoff

A sequence diagram. The browser posts one multipart form to the Worker. The Worker uploads the image to the Gradio Space, submits the job, gets an event id back, opens the server-sent event stream for it, and returns that response body as its own without reading it. The events then flow from the Space through Cloudflare to the browser, which parses the complete event and pulls the video URL out.

The Worker does four short calls and then gets out of the way. It opens the upstream event stream and hands that response body back as its own, unread, so Cloudflare is just piping bytes between two sockets it already holds. A sixty second generation costs the Worker the same as a one second one.

The price is that the browser has to speak SSE. web/src/scripts/studio.ts buffers the stream, splits it on blank lines, and waits for the complete event to pull the video URL out of the return tuple. That file and worker/src/index.ts are two halves of one contract.

Run locally

You need Node 20+, pnpm 9+, and a GitHub clone of this repo. Both lockfiles are v9, which is what CI installs with.

# Frontend (Astro dev server on :4321)
cd web
pnpm install
pnpm dev

# Worker proxy (Wrangler local sandbox on :8787) in a second terminal
cd worker
pnpm install
pnpm wrangler dev

Open http://localhost:4321 and try a generation. The frontend reads PUBLIC_WORKER_URL from web/.env, defaulting to http://localhost:8787 which matches wrangler dev.

Deploy

The static site auto-deploys to GitHub Pages on every push to main via .github/workflows/deploy.yml. The Worker is typechecked by .github/workflows/worker.yml but deployed manually with Wrangler:

cd worker
pnpm wrangler login
pnpm wrangler deploy

After the first Worker deploy, set the resulting URL as a PUBLIC_WORKER_URL repository variable under Settings → Secrets and variables → Actions → Variables. The next Pages build will wire the frontend to your production Worker.

Limitations

  • Duration caps at 4.5 seconds at ~720p, in the slider and again in the Worker. The free upstream Space hits its wall-clock limit around 5 seconds and returns nothing, so the cap sits just under it, and it is enforced at the public endpoint rather than only in the page. Long shots and complex multi-character scenes are out of scope.
  • The free HF Space is shared, so first calls cold-start (60 to 120 seconds) and peak hours queue up.
  • The upstream Space could change or disappear. If it does, the Worker's HF_SPACE_BASE env var is the only line to update.
  • Workers free tier has wall-clock limits that occasionally clip very long generations. Retry usually works.

Roadmap

  • Streaming SSE for live progress updates in the UI
  • Optional second keyframe ("last frame") for guided motion
  • Saved generations across sessions (opt-in localStorage)
  • Frame extraction tool to use a generated frame as the next input
  • Swap-able backend: fal.ai for users with API keys

Credits

License

MIT. See LICENSE.


Part of the NiftyAi project family by Igor Lima. Companion to NiftyStats.

About

Animate a still image with Wan 2.2. Free, open source, runs in your browser.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages