Back to cookbook
EZ

Eric Zakariasson

Added ago

TypeScript · Intermediate

Storyboard to Short Film

View as Markdown

This app builds an AI short film generator with the Grok API and Grok Imagine that turns a one-line premise into a narrated four-shot film. It shows how to plan shots as structured output, keep a character consistent across keyframes, and animate them with image-to-video.

Give it a premise, and it plans four shots, draws a keyframe for each, animates them into eight-second clips, and narrates the story. A small web app shows each step as it happens, from Grok's reasoning to the finished film.

What you'll learn

  • Plan a shot list as structured output, and stream Grok's reasoning while it plans
  • Keep a character consistent across images by editing the first keyframe, while giving each shot its own scene and camera angle
  • Animate still images with image-to-video, running the jobs in parallel and waiting for them to finish
  • Add narration with text-to-speech
  • Stream progress from a Node server to a web page with server-sent events

Run it

You need Node.js 22.13 or later. Put your API key in .env at the root of the repo, or export XAI_API_KEY. If ffmpeg is installed, the app also joins the shots into one film with the narration mixed in.

Bash

cd examples/storyboard-to-film/typescript
npm install
npm run web

Open http://localhost:3000, type a premise or pick one, and click Make film. The page works like a video editor. The plan fills the panel on the left, with Grok's reasoning while it writes it. Each shot fills its slot on the timeline as its keyframe is drawn and its video renders, and its line of narration fills the voice track below. Click a shot to watch it in the monitor. When the shots are joined, the film plays in the monitor, and clicking the timeline jumps through it.

To make a film from the terminal instead, pass a premise:

Bash

npm start -- "A lighthouse keeper's cat on the night of the big storm"

Without a premise, it films a paper boat's journey through a rainy city. Both save everything to output/<title>/, and the terminal version also writes an index.html there that plays the shots. A run makes four images and four videos, takes a few minutes, and cost about $5 when we ran it. Both show the image and video cost at the end.

How it works

The shared code is in src/storyboard.ts:

  1. planStoryboard() streams a title, a visual style, and four shots from grok-4.7 as structured output, each shot with a scene, a camera move, and a line of narration. Each scene has its own setting and camera angle. It reports Grok's reasoning as it arrives.
  2. drawKeyframes() generates the first keyframe with client.images.generate(), then makes the other three with client.images.edit(), using the first only as a reference for the subject and style and asking for a new scene from a new camera angle. Editing is what keeps the main character looking the same from shot to shot. Asked only to keep the subject, an edit also keeps the composition, and the shots look like versions of one picture. It reports each keyframe as soon as it's drawn.
  3. animate() starts a grok-imagine-video-1.5 job for a keyframe with client.videos.generate() and waits for it with client.videos.wait(). The videos come with their own sound.
  4. narrate() records a line of narration with client.voice.speak().
  5. assembleFilm() uses ffmpeg to lay each line of narration over its shot, with the shot's own sound turned down, and joins the shots.

The functions take an AbortSignal, which they pass to the SDK and to ffmpeg, so a caller can stop them partway. drawKeyframes() and animate() also return what each image and video cost.

src/server.ts runs these steps for the page. It animates the four shots and records the narration at the same time, saves each file to output/<title>/ as soon as it's ready, and streams its progress to the page as server-sent events. It serves only the files it saved, in byte ranges so the browser can seek in the videos, and stops making API calls if the page is closed. public/index.html is plain HTML and JavaScript that shows those events. src/index.ts does the same work in the terminal.

More from the cookbook