Showing posts with label animation. Show all posts
Showing posts with label animation. Show all posts

2026-07-12

PAM's first feature animation.

PAM stands for Poses Audio Motion (pam github repo). See the reference manual for more.

Stills from a nerdy movie, and the even nerdier way it was made.

The animation embedded below was not edited together in the usual sense. Nobody dragged a clip onto a timeline, nudged a keyframe, or clicked a render button in an animation package. The entire film — every character, prop, line of dialogue, camera focus, scene transition, and sound cue — is described in a single text file: a screenplay written in JSON. A Python module reads that file and produces the finished video. The movie is compiled, not edited.

"Too Nice to Die" on Youtube

Click the above link to watch the 100 minute film.

The screenplay is the source code

The screenplay is an ordered list of actions. Here is an actual excerpt — a character enters, walks to her mark, and speaks:

{"action": "fade_in", "who": "nona", "offset": [-6.0, -2.0, 0.0]},
{
   "action": "walk_to", 
   "who": "nona", 
   "x": -2.25,
   "sfx": "sfx/cue-nona.mp3", 
   "sfx_duration": 2.0
},
{
   "action": "say", 
   "who": "nona",
   "text": "Chekov will confirm.",
   "hold": 2.0,
   "sfx": "sfx/cue-nona.m4a", 
   "sfx_duration": 1.0
}

That (and about 45000 lines not shown here:-) is the whole authoring interface. There is no hidden project file, no binary scene format, no GUI state. If it isn't in the JSON, it isn't in the movie. Even the sound design works this way: each action can carry an inline sound cue (with gain and trim parameters), so the film's roughly 1600 audio events are just more keys in the same text file.

The player: one command, whole film

The Python module that interprets the screenplay is PAM (Python Animation Maker), built on top of the Manim mathematical animation library — yes, the one used for math-explainer videos. PAM adds stick-figure characters with poses, faces, walk cycles, speech bubbles, props, a dog, and a talking dodecahedron, all driven by the action vocabulary above. Rendering the feature is a single shell command:

PAM_SCRIPT=SCREENPLAY_FILENAME manim --disable_caching -ql pam_player.py PAMPlayer

No mouse. No further keyboard input. Some time later, out comes an MP4 with the soundtrack already mixed in.

Why do it this way?

The film is versionable. The screenplay lives in a git repository like any other source file. A change to the movie is a diff — readable, reviewable, revertible. When I recently tightened all the scene transitions, the entire edit was a script transforming one JSON file into another, and it could be verified the same way software is verified: by checking, mechanically, that every scene still starts with exactly the same characters on stage as before.

The film is reproducible. Anyone with the repository and the sound files can render the identical movie. There is no "project file that only opens on my machine."

Fixes are surgical. When a character faced the wrong way in one scene — a stick figure looking left while talking to a dog standing to her right — the fix was changing the string "lside" to "rside" on two lines. Re-render, done.

And honestly: it's the natural medium. A screenplay was already a formal, structured document before computers existed. Sluglines, action lines, dialogue — screenwriters invented a domain-specific language a century ago. JSON just makes the compiler possible.

The pipeline, end to end

Scenes start life in Fountain, the plain-text screenwriting format, extended with annotation keys for moods, camera focus, sounds, and captions. A converter (fountain2pam.py) turns that into the JSON action list; the player turns the JSON into video; the video goes to YouTube. Text all the way down, until the last step.

The screenplay for the film above, the player, and the reference manual are in the PAM repository if you want to see how deep the rabbit hole goes — or render your own. Bring your own dodecahedron.

2026-03-28

A fountain+ guide

Fountain+ syntax guide

This is a guide to an extension of the standard screenplay format fountain that, hopefully, will be useful to animators. This is a work in progress (currently at version 0.9.0). For one fountain+ example, click here. For the corresponding animation, check out this touching love story:-), Lucy meets Lenny (youtube, 71 seconds).

The basic idea is to hide carefully crafted syntax that AI video generators (such as flow or kling) can use for generating accurate animations. The [[ KEY: value ]] notes are embedded in the fountain file so they can be read by the converter (fountain2pam, which uses new python functions as well as the parsing of screenplain) to filter out prop, motion cues, and other blocking information. These fountain+ additions are valid fountain notes (hidden by standard renderers such as highland or screenplain or afterwriting), so they do not interfere with the standard screenplay format.

In other words, fountain+ exists to enrich and extend a standard .fountain screenplay so that fountain2pam.py can generate both better PAM JSON blocking and higher-quality AI video prompts (https://github.com/wdjoyner/pam). The idea is simple: put richer production metadata into the screenplay file itself so the same source file can drive PAM animation, prompt generation, and later video assembly.

The guide below was written with the help of sonnet 4.6 (anthropic) and chatGPT (openai).

Basic syntax

[[ KEY: value ]]

Notes may span multiple lines:

[[ KEY: first line
   continuation line ]]

Keys are case-insensitive and terminate at the first colon.

Supported keys

Key Scope Effect
MOOD Scene Visual tone appended to every subscene prompt
SCENE POPULATION Scene / mid-scene Character presence note for AI prompt generation
NEGATIVE Scene / mid-scene Negative prompt text
CAMERA Scene / mid-scene Camera direction override
KIND File Species/type template for character descriptions

MOOD

Use MOOD immediately after a scene heading to specify visual tone, lighting, palette, and general emotional register for all subscenes in that scene.

INT. VENUS CITY OBSERVATORY - NIGHT

[[ MOOD: cool blue-green, holographic, bureaucratic-noir ]]

Use 3–5 strong descriptive terms rather than vague labels.

SCENE POPULATION

Use SCENE POPULATION to tell the converter which characters are present at a given point in the scene. This is especially useful for AI video generation, because it helps prevent missing or hallucinated characters in a shot.

[[ SCENE POPULATION: Governor, Sidel. No other characters. ]]

Update it whenever characters enter or exit:

[[ SCENE POPULATION: Governor, Sidel, then Nona enters. ]]
[[ SCENE POPULATION: Sidel, Nona only. Governor exits here. ]]

In practice, this works best when paired with an updated NEGATIVE note so the active prompt and the “do not render” guidance stay aligned.

NEGATIVE

Use NEGATIVE to supply explicit negative-prompt text for image or video generators.

[[ NEGATIVE: No additional human figures. No crowd. No extras.
   No faces on the dodecahedron. ]]

Update it after entrances or exits:

[[ NEGATIVE: No dodecahedron. No geometric objects.
   No additional human figures. ]]

CAMERA

Use CAMERA when you want to override the converter’s default shot choice.

[[ CAMERA: Wide establishing shot. ]]
[[ CAMERA: slow push in toward the Governor during this exchange ]]
[[ CAMERA: over-the-shoulder from Sidel's perspective ]]

When CAMERA is present, it takes priority over automatic camera heuristics.

KIND

KIND defines a reusable species/type template for character appearance. Place these notes anywhere in the file; they are file-level, not tied to a single scene.

[[ KIND: Venusian | short, green-skinned humanoid, wide-waisted,
   large dark eyes, minimal body hair ]]
[[ KIND: talking dog | four-legged, golden retriever coloring,
   expressive face, wears a small bow tie ]]

Tag a character with a kind on the intro line:

NONA SONNOF [Venusian] — short, early 50s, formidable...
RAMIS [Dog], a compact robot dog with silver-grey joints, trots in.

The converter uses the kind template as a species/type baseline and combines it with the character’s own description.

Prop-character routing via [Kind]

Characters tagged as non-humanoid or special prop-characters can be routed to non-HumanGraph representations when appropriate.

RAMIS [Dog], a compact robot dog with silver-grey joints, trots in.

This allows dialogue to route to prop_say and movement to the proper non-humanoid action such as trot_to.

Complete scene opening example

INT. VENUS CITY OBSERVATORY - NIGHT

[[ MOOD: cool blue-green, holographic, bureaucratic-noir ]]
[[ SCENE POPULATION: Governor, Sidel. No other characters
   until Nona enters at her cue. ]]
[[ NEGATIVE: No additional human figures. No crowd. No extras.
   No faces on the dodecahedron. ]]

The room is a domed observatory. Cool blue-green light from slowly
orbiting holographic planets. Foreground: a long conference table
with a computer terminal. Background: two robot sentinels at sealed
blast doors, status lights blinking amber.

Practical guidance for stronger prompts

1. Put MOOD under the scene heading

Use 3–5 words covering palette, lighting style, and emotional register.

INT. HOSPITAL CORRIDOR - DAY

[[ MOOD: cold white fluorescent, clinical, quietly tense ]]

2. Write the opening action block like a cinematographer

Go near → far, mention the light source early, and end on the overall mood impression.

The room is a domed observatory. Cool blue-green light from slowly
orbiting holographic planets. Foreground: a long conference table
with a computer terminal. Midground: star maps covering the curved
walls. Background: two robot sentinels at sealed blast doors,
status lights blinking amber. The air feels bureaucratic and
slightly ominous.

3. On character introduction, give build/age, wardrobe, and posture

SERGEANT SIDEL [Venusian] — compact, mid-40s, the kind of face
that has followed orders for twenty years and found it agreeable.
Classic Venusian military dress uniform: deep cobalt blue, high
collar, gold piping at the shoulders and cuffs, regulation boots.
Stands at attention: chin up, arms at sides, eyes forward.

4. For prop-characters, describe size, surface, glow behavior, and states

The GOVERNOR OF VENUS — a slowly rotating dodecahedron roughly the
size of a basketball, hovering at eye level above the conference
table. Translucent gold, glowing from within. Each face catches
light differently as it turns. It pulses brighter when speaking.
It goes amber-orange in low-power mode. It goes dark when it exits.
It has no face and needs none.

5. For entrances, describe silhouette, wardrobe, entrance energy, and first gesture

NONA SONNOF [Venusian] — short, early 50s, formidable in the way
that small objects under high pressure are formidable. Futuristic
Venusian business suit: structured but fluid, deep charcoal with
subtle iridescent trim that shifts color in the light. She sweeps
in through the blast doors with the energy of someone who owns
every room she enters.

6. Use parentheticals for gaze or body orientation, not just tone

NONA
(not looking at Sidel — eyes on the Governor)
Every time one fails, the hospital fills up.

7. For “unanimatable” lines, write what the camera sees

A beat. The holographic Earth diagram pulses quietly behind them.
Nobody moves. The room hums.

Anything PAM cannot map directly into blocking may still enrich the AI prompt output.

8. For prop color changes, include color, motion change, and dramatic meaning

The dodecahedron's glow dims from gold to a flat amber-orange.
Its rotation slows. A power-conservation mode — the AI equivalent
of someone putting a hand up and saying "one moment."

9. For on-screen text, add a lead-in line

The dodecahedron's surface turns a corporate amber. Then, in
clean sans-serif:

> PLEASE WAIT...
> THE GOVERNOR OF VENUS
> WILL BE RIGHT WITH YOU.

10. End scenes with a clear final image

Describe what still moves and what emotional scale remains.

Nona stares at the empty air where the Governor was. The
holographic planets continue their silent orbits above her.
She looks very small in the room.

Quick reference card

What you're writing Rule of thumb
Scene heading Put [[ MOOD: ... ]] immediately below
Species / type Use [[ KIND: name | description ]] anywhere in file
Character intro [Kind] tag, then build/age, wardrobe, posture
Prop-character intro size, surface, glow behavior, color states
Entrance silhouette, wardrobe, entrance energy, first gesture
Parenthetical eye contact or body orientation, not just tone
Unanimatable action write what the camera sees
Prop color change color, motion change, dramatic meaning
On-screen text add a context lead-in line
Final image say what remains moving and what the emotional scale is
Population change update SCENE POPULATION and NEGATIVE together
Camera override add [[ CAMERA: ... ]] before the relevant beat
Small accessories remove if they cause generator inconsistency

Subscene prompts

The --prompts output contains per-subscene video prompts in a four-paragraph cinematic format:

[SHOT / CAMERA]          — framing and camera movement
[SETTING / ATMOSPHERE]   — environment, lighting, mood
[CHARACTERS & ACTION]    — who does what, in what order
[DRAMA / CUT]            — what the scene is building toward

Clip modes:

Mode Boundary rule Best for
per-speaker (default) New clip per speaker change Kling and similar
timed Drama-aware 5–10 second windows Strong-consistency generators

2026-03-22

A fun stick figure animation

This post is on a work-in-progress called PAM. PAM is a Pose, Audio, Motion library for manim (here Audio really means text-in-a-speech-bubble at this point). See https://github.com/wdjoyner/pam for a detailed readme and example code. PAM animates humanoid graphs against a black background.

The code from a specific type of json file called a PAM screenplay is fed into a python module (pam_player.py) directing the animation. The output is below (click on the lower corner to enlarge).