Overview
Most of what makes a video in Imaginary Arts is generated: a still image for each part of the narration, a short clip where movement matters, a reference picture of a recurring character so she looks the same in every shot. This section is about producing that material. This page is the map; the following pages go into images, clips, references, and iterating.
What you get
- Images, generated by Nano Banana 2, in the shape of your canvas and in a chosen art style.
- Clips, generated by Veo 3.1 or Seedance 2.5 from a still image plus a description of the motion, from 4 up to 30 seconds long depending on the model, optionally with their own sound.
- References, images of a character, prop or place that later generations are told to match, so the same face and the same coat appear in every scene.
Everything is generated once, saved in the project's assets folder, and reused on every later run until you change its inputs. See Project and Core Concepts.
Two ways to write a prompt
You can write every prompt yourself, or describe the video and let the library write the prompts.
Yourself. An ImageAssetBuilder or VideoAssetBuilder holds a prompt you wrote, and AssetsBuilder generates it. Full control, no surprises. This is the whole story for a single picture, a piece of b-roll, or anyone who likes writing prompts.
images = AssetsBuilder(
project=project,
direction=ImageDirection(art_style=ImageDirectionStyle.Realistic()),
assets=[
ImageAssetBuilder(prompt="A lighthouse on a rocky cliff at dusk, waves below, seen from the shore"),
],
).build()With the scene writer. For a narrated video with ten or twenty scenes, writing ten consistent prompts is the tedious part. ScenesBuilder takes what each scene says and a little art direction, and one language-model call writes an image prompt for every scene (and a motion prompt for video scenes), consistent with each other and with the references you declared. Those authored scenes then feed both the voiceover and the asset builders.
scenes = ScenesBuilder(
project=project,
type="storyline",
direction=ImageSceneDirection(environment="Miletus, Ionia, sixth century BC"),
scenes=[
ImageSceneBuilder(voiceover="Thales of Miletus asked what everything is made of.",
direction=ImageSceneDirection(pointer="Thales on the shore, looking at the sea")),
ImageSceneBuilder(voiceover=["His answer was water.", "He was wrong."],
direction=ImageSceneDirection(pointer="A cup of water held up to the light")),
],
).build()
voiceover = create_voiceover(scenes.script, voice=Voices.BritishNarrator, project=project)
images = AssetsBuilder(
project=project,
direction=ImageDirection(art_style=ImageDirectionStyle.EuroStorybook()),
assets=[ImageAssetBuilder.from_scene(s) for s in scenes],
).build()The two mix freely. A scene with a prompt= you wrote is pinned: the writer leaves it alone and treats it as context when writing the others.
The pipeline
ScenesBuilder -> Scenes prompts, one per scene (optional)
ReferencesBuilder -> References one image per character / prop / place (optional)
AssetsBuilder -> Assets the images and clips
VideoBuilder -> mp4 placement, voice, captions, lookEach stage is cached on its own, and each can be replaced by hand-written input: a literal script instead of Scenes.script, a prompt you wrote instead of an authored one, a photo instead of a generated reference.
Directing the scene writer
A direction tells the writer what a scene should show. There are two ways to fill one, and a single direction uses one or the other:
- A pointer, free text:
ImageSceneDirection(pointer="The other team celebrating."). - Structured fields:
subject,action,environment,art_style,lighting,details. A video scene'sVideoSceneDirectionaddsmotion(what changes during the clip) andaudio(what the soundscape is).
Directions exist at two levels. The one on ScenesBuilder is global and applies to the whole video, such as the setting and the era. The one on each scene is specific to it. They are shown to the writer side by side; nothing is merged or inherited. Every authored scene needs at least one field set; a pinned scene needs none.
Scenes and what they carry
ImageSceneBuilder(voiceover=None, direction=ImageSceneDirection(), prompt=None, *, name=None, take=1)
VideoSceneBuilder(voiceover=None, direction=VideoSceneDirection(), prompt=None, video_prompt=None,
*, name=None, take=1)voiceoveris what the scene says: a string, a list of clauses, orNonefor a silent scene. The same shapes ascreate_voiceovertakes, becausescenes.scriptis passed straight to it.promptpins the scene to a prompt you wrote. A video scene pins with bothpromptandvideo_prompt.namelabels the scene's files, and flows to assets built withfrom_scene.takere-authors just this scene.
build() prints what it is about to do, plan: scenes — 2 to author, 8 cached, 1 pinned, makes one call for the scenes that need writing, and returns Scenes. Index it, iterate it, and use scenes.script for the voiceover. Each built scene has .prompt, and a video scene .video_prompt.
Art styles
Generated images share an art style, set with ImageDirection(art_style=...) on AssetsBuilder and overridable per asset.
| Style | Character |
|---|---|
ImageDirectionStyle.Realistic() | Photographic. For explainers, news, anything factual. |
ImageDirectionStyle.DarkAcademia() | Oil-painting gloom, libraries and candlelight. |
ImageDirectionStyle.AnimeLofi() | Soft anime illustration with a muted, lo-fi palette. Calm and nostalgic. |
ImageDirectionStyle.Minimalist() | Flat shapes, few colors, lots of space. |
ImageDirectionStyle.Ember() | Warm, high-contrast, fire and dusk. |
ImageDirectionStyle.EuroStorybook() | Ink and watercolor, European picture-book illustration. |
ImageDirectionStyle.OrientalLacquerInk() | Lacquer-black backgrounds, gold and vermilion ink. |
ImageDirectionStyle.Custom(prefix, title="custom") | Your own style description, prepended to every prompt. |
The style is a description added in front of every image prompt. It never touches a clip's motion prompt, which describes only movement.
Cost and concurrency
Generation costs money, so AssetsBuilder.build() prints its plan before starting:
plan: images — 2 to generate (3 image calls incl. edits), 5 cached
plan: videos — 2 to generate (+2 keyframes), 0 cached — est. video cost ~$4.40Clips are the expensive part. Roughly, Veo 3.1 costs $0.20 per second silent and $0.40 with sound; Seedance 2.5 costs $0.22 per second at 480p and $0.47 at 720p, sound included. Up to five generations run at once, each failed one is retried once, and every result is written to disk only when complete, so a crash never leaves a half-written file.