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.

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-trill.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 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 1,600 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.

No comments: