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, look

Each 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's VideoSceneDirection adds motion (what changes during the clip) and audio (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)
  • voiceover is what the scene says: a string, a list of clauses, or None for a silent scene. The same shapes as create_voiceover takes, because scenes.script is passed straight to it.
  • prompt pins the scene to a prompt you wrote. A video scene pins with both prompt and video_prompt.
  • name labels the scene's files, and flows to assets built with from_scene. take re-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.

StyleCharacter
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.40

Clips 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.