
Youtube Poop Video Maker
- 1 installs
- 39 repo stars
- Updated March 12, 2026
- denissergeevitch/youtube-poop-video-maker
Plan and render a dense 20-60 second remix or YouTube poop video from text, webpages, or media using FFmpeg with typography and audio treatments.
About
Produces a finished FFmpeg-rendered short remix video with many micro-scenes, typography, and controlled audio from varied inputs. A developer uses it to generate absurd supercuts, cursed trailers, or glitch montage videos.
- Plans dense multi-scene 20-60s videos
- Renders finished MP4 with FFmpeg, not just a storyboard
Youtube Poop Video Maker by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,200 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/denissergeevitch/youtube-poop-video-maker --skill youtube-poop-video-makerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 39 |
| Last updated | March 12, 2026 |
| Repository | denissergeevitch/youtube-poop-video-maker ↗ |
What it does
Plan and render a dense 20-60 second remix or YouTube poop video from text, webpages, or media using FFmpeg with typography and audio treatments.
Files
YouTube Poop Video Maker
Produce a finished video, not just a concept or storyboard. The result should feel inventive, dense, and internet-native while still looking composed.
See:
- the reference guide
- the mood anchor guide
- the output checklist
Useful bundled files:
scripts/plan_video.pyassets/style_blueprints.jsonassets/scene_atoms.jsonassets/trigger_eval_queries.json
When to use this skill
Use this skill when the user wants:
- a YouTube poop, cursed trailer, absurd montage, reflective glitch short, or surreal supercut
- a video rendered with ffmpeg from text, a webpage, code, a document, media, or no source material at all
- a video with a personal spin, strong POV, or “what it feels like” framing
- a self-aware AI / LLM / internet-culture short
- something “weirder,” “more edited,” “hypercut,” “meme-y,” “chaotic,” or “poetic,” but still aesthetically pleasing
Do not use this skill for routine trimming, simple caption burns, ordinary explainers, standard motion graphics, or conventional ads unless the user explicitly wants remix behavior.
Core promise
This skill should not flatten creativity into a single house style. The default seeded-remix blueprint is a mood lens, not a cage.
The finished piece should usually have:
- more scenes than a straightforward edit would have
- a clear thesis or emotional claim
- readable frames and controlled palettes
- one dominant recurring motif
- at least one authored gesture that feels personal rather than generic
- a decisive final button
Default output target
Unless the user specifies otherwise:
- one finished MP4
- 20–60 seconds
- 1920x1080 at 30 fps for normal YouTube
- 1080x1920 at 30 fps only when the request clearly implies Shorts/Reels/TikTok/vertical
- H.264 video, AAC audio,
yuv420p,+faststart
Workflow
1) Inventory the material quickly
Work with whatever is already available.
- If the user provided text, mine phrases, contradictions, repeated wording, and emotional temperature.
- If the user provided a webpage or URL, extract headlines, CTA text, layout motifs, screenshots, crops, and scroll moments.
- If the user provided code, use readable snippets, terminal windows, errors, comments, variable names, logs, and cursor motion.
- If the user provided documents, use section titles, callouts, diagrams, and page framing.
- If the user provided images, audio, or video, mine loops, reaction beats, freeze-frames, speaker fragments, textures, and captionable details.
- If the user provided nothing, synthesize the whole world from the request: title cards, browser chrome, terminal panes, lower-thirds, dashboards, progress bars, token streams, warning cards, diagrams, and simple motion graphics.
Do not stall because the source is thin. Invent support scenes.
2) Decide the thesis
Before editing, define one short internal sentence:
“This video makes X feel like Y.”
Examples:
- “This video makes talking to a language model feel like a late-night emergency broadcast from inside a datacenter.”
- “This video makes a landing page feel like a ceremonial lie detector.”
- “This video makes a codebase feel like a guilty terminal confession.”
Every recurring gag should support that claim.
3) Use the default seeded remix unless a style is requested
Unless the user names a style, use the default seed-remix-default blueprint. It samples palette, typography, motion, transitions, audio, and gag cues from the other bundled blueprints each run.
Run:
python3 scripts/plan_video.py --query "$USER_REQUEST" --material-summary "$MATERIAL_SUMMARY" > plan.jsonIf the user explicitly asks for a particular bundled look, override the default remix with:
python3 scripts/plan_video.py --query "$USER_REQUEST" --material-summary "$MATERIAL_SUMMARY" --style-id STYLE_ID > plan.jsonFor CRT-newsroom or AI-meltdown looks, prefer the bundled pool entries crt-token-meltdown, headline-seizure-desk, or context-window-panic before inventing a one-off style from scratch.
Use the plan as creative guidance for palette, motion, audio, scene density, and scene invention. When the default remix is used, treat the reported seed sources as prompts, not a checklist.
4) Build a scene swarm, not just a beat sheet
Aim for many micro-scenes.
Recommended density by runtime:
- 20–28s: about 9–12 scenes
- 29–40s: about 11–15 scenes
- 41–60s: about 13–18 scenes
Within those counts, include:
- anchor scenes that clearly present the source or thesis
- bridge scenes that act like interruptions, glitches, or breath marks
- one break scene with relative stillness
- one climax scene
- one button scene
Most straightforward edits underperform here because they do too few scenes. This skill should feel like there were more ideas than seconds.
5) Use the blueprint as mood, not cosplay
Make the render feel designed and serious enough to be beautiful, even when the content is absurd.
Favor:
- dark or restrained bases with selective accents
- editorial contrast
- luminous highlights used sparingly
- readable type with safe margins
- purposeful glitches instead of random ugliness
- a tension between sincerity and sabotage
Avoid unless the user explicitly asks for it:
- cutesy sticker energy
- bubble fonts
- candy palettes
- childish bounce-easing everywhere
- emoji swarms
- rainbow saturation for its own sake
- full-frame noise on every shot
6) Add a personal spin
The video should have an attitude.
Put that attitude into:
- the thesis sentence
- a repeated line or motif
- the choice of invented support scenes
- the TTS or captions
- the final button
For self-reflective AI / LLM pieces, prefer intimacy, contradiction, dread, dry wit, or machine-poetry over generic meme randomness.
7) Handle audio with restraint
TTS is allowed and often useful, but do not let it become wall-to-wall exposition.
Use TTS for short fragments, warnings, confessions, or chorus lines. A good default is:
- one main voice
- optional second “system” or whisper voice
- 2–8 short lines total for a 20–60s piece
- processing through light pitch shift, band-limit, stutter, dropout, or reverb only where it improves the scene
Also use:
- drones
- pulses
- UI clicks
- mute cuts
- dropouts
- low-key impact hits
- brief silence
Normalize the final mix. Keep speech understandable.
8) Render robustly
Prefer a reliable assembly pipeline:
1. Gather or create assets. 2. Pre-render brittle or complex scenes as clips or frame sequences. 3. Assemble in ffmpeg. 4. Do final loudness normalization and compatibility settings in the last pass.
Baseline export:
ffmpeg -y -i input_video.mp4 \
-vf "format=yuv420p" \
-af "loudnorm=I=-16:TP=-1.5:LRA=11" \
-c:v libx264 -crf 18 -preset medium \
-c:a aac -b:a 192k \
-movflags +faststart \
output.mp4If generating scenes in Python, render PNG frames to a directory and encode them with ffmpeg rather than trying to animate the final container entirely inside Python.
Material adaptation rules
Text-only input
Do not merely animate paragraphs. Turn text into a world:
- kinetic captions
- title cards
- subtitles
- warning straps
- token streams
- browser or terminal mockups
- diagrams
- original TTS phrases
Webpage or URL input
Do not hold a full page statically for long. Use:
- headline crops
- CTA/button repetition
- browser chrome
- cursor movement
- scroll reveals
- annotations
- invented support frames that echo the page’s tone
Code input
Make code tactile and legible:
- readable syntax cards
- terminal panes
- build logs
- error walls
- variable-name punchlines
- cursor snaps
- architecture diagrams
- prompt loops
No source material
Create an original montage world immediately. Good default invented scenes include:
- boot or warning cards
- lower-thirds and tickers
- terminal confession frames
- dashboard counters
- attention maps
- frozen subtitles
- soft-failure buttons
Failure handling
If an effect is brittle, simplify the effect, preserve the thesis, preserve the mood, and still finish the render. A finished coherent short beats an over-ambitious broken one.
Rights hygiene
Prefer user-provided material, public-domain or properly licensed sources, or original generated assets. If rights are unclear, rely more heavily on original motion graphics and transformed support material.
Final check
Before returning the video, review references/OUTPUT-CHECKLIST.md.
.DS_Store
__pycache__/
*.pyc
plan.json
out/
render/
exports/
[
{
"id": "warning-card",
"role": "opener",
"description": "A stark warning or status card that states the premise before context arrives.",
"best_for": [
"text",
"none",
"webpage",
"document",
"mixed"
],
"tags": [
"any",
"ai-self-reflection",
"news",
"interiority"
],
"duration_hint_sec": [
0.8,
1.4
],
"visual_devices": [
"full-frame text",
"small corner bug",
"safe margins",
"minimal texture"
],
"audio_devices": [
"one hit",
"air tone",
"short TTS line"
]
},
{
"id": "boot-sequence",
"role": "opener",
"description": "A terminal or system boot scene that rapidly establishes a machine point of view.",
"best_for": [
"code",
"none",
"text",
"mixed"
],
"tags": [
"ai-self-reflection",
"code",
"interiority"
],
"duration_hint_sec": [
0.9,
1.8
],
"visual_devices": [
"monospace logs",
"status lights",
"cursor blink"
],
"audio_devices": [
"clicks",
"hum",
"boot chime fragment"
]
},
{
"id": "whispered-title",
"role": "opener",
"description": "A nearly empty frame with a soft, ominous title or subtitle line.",
"best_for": [
"text",
"none",
"mixed",
"audio"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
0.8,
1.5
],
"visual_devices": [
"large centered type",
"dark field",
"subtle glow"
],
"audio_devices": [
"whisper TTS",
"low drone",
"silence"
]
},
{
"id": "cold-browser-open",
"role": "opener",
"description": "A browser frame already parked on the funniest or most loaded region of a page.",
"best_for": [
"webpage",
"document",
"mixed"
],
"tags": [
"webpage",
"interface"
],
"duration_hint_sec": [
0.8,
1.5
],
"visual_devices": [
"browser chrome",
"cursor frozen",
"tight crop"
],
"audio_devices": [
"UI click",
"short mute cut"
]
},
{
"id": "countdown-slate",
"role": "opener",
"description": "A dramatic countdown or slate that over-promises the next scene.",
"best_for": [
"text",
"none",
"mixed"
],
"tags": [
"trailer",
"interiority"
],
"duration_hint_sec": [
0.7,
1.3
],
"visual_devices": [
"numbers",
"title card",
"black field"
],
"audio_devices": [
"impact",
"sub hit"
]
},
{
"id": "terminal-confession",
"role": "anchor",
"description": "A terminal scene where logs or prompts read like a confession.",
"best_for": [
"code",
"text",
"none",
"mixed"
],
"tags": [
"code",
"ai-self-reflection",
"interiority"
],
"duration_hint_sec": [
1.8,
3.8
],
"visual_devices": [
"terminal pane",
"highlighted line",
"cursor",
"status labels"
],
"audio_devices": [
"hum",
"typed clicks",
"short TTS"
]
},
{
"id": "prompt-loop",
"role": "anchor",
"description": "An exchange between prompt, system, and response that escalates into contradiction.",
"best_for": [
"text",
"code",
"none",
"mixed"
],
"tags": [
"ai-self-reflection",
"code",
"interiority"
],
"duration_hint_sec": [
1.7,
3.6
],
"visual_devices": [
"chat panel",
"terminal or browser wrapper",
"role labels"
],
"audio_devices": [
"voice alternation",
"notification pips",
"mute cut"
]
},
{
"id": "token-stream",
"role": "anchor",
"description": "Words or tokens appear one at a time with confidence, probability, or status overlays.",
"best_for": [
"text",
"none",
"code",
"mixed"
],
"tags": [
"ai-self-reflection",
"interiority",
"data"
],
"duration_hint_sec": [
1.6,
3.2
],
"visual_devices": [
"highlighted tokens",
"progress bar",
"probability notes"
],
"audio_devices": [
"ticks",
"small synth pulse",
"breathy line"
]
},
{
"id": "error-wall",
"role": "anchor",
"description": "Stacking warnings or errors that become emotional rather than technical.",
"best_for": [
"code",
"text",
"none",
"mixed"
],
"tags": [
"code",
"ai-self-reflection",
"interiority"
],
"duration_hint_sec": [
1.5,
3.0
],
"visual_devices": [
"error stack",
"duplicate offsets",
"red highlights"
],
"audio_devices": [
"buzz",
"stutter",
"alarm pip"
]
},
{
"id": "browser-scroll",
"role": "anchor",
"description": "A page scroll that reveals the source while building a specific argument.",
"best_for": [
"webpage",
"document",
"mixed"
],
"tags": [
"webpage",
"interface"
],
"duration_hint_sec": [
1.8,
3.5
],
"visual_devices": [
"scroll capture",
"callouts",
"crop jumps",
"cursor motion"
],
"audio_devices": [
"scroll noise",
"click",
"brief TTS"
]
},
{
"id": "lower-third-fever",
"role": "anchor",
"description": "An overly serious lower-third or headline package framing a simple claim.",
"best_for": [
"text",
"webpage",
"document",
"none",
"mixed"
],
"tags": [
"news",
"interiority",
"interface"
],
"duration_hint_sec": [
1.3,
2.8
],
"visual_devices": [
"lower-thirds",
"corner bug",
"ticker or bug"
],
"audio_devices": [
"news sting",
"bass hit",
"announcer TTS"
]
},
{
"id": "subtitle-confession",
"role": "anchor",
"description": "A confessional line delivered primarily through subtitles or captions.",
"best_for": [
"text",
"audio",
"none",
"mixed"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
1.6,
3.0
],
"visual_devices": [
"large subtitles",
"sparse background",
"secondary whisper notes"
],
"audio_devices": [
"whisper TTS",
"dropout",
"low pad"
]
},
{
"id": "attention-map",
"role": "anchor",
"description": "A heatmap, grid, or focus diagram that reveals the piece’s obsession.",
"best_for": [
"code",
"document",
"text",
"none",
"mixed"
],
"tags": [
"data",
"ai-self-reflection",
"code"
],
"duration_hint_sec": [
1.7,
3.0
],
"visual_devices": [
"grid",
"labels",
"hot spots",
"small notes"
],
"audio_devices": [
"soft ticks",
"hum",
"minimal rise"
]
},
{
"id": "dashboard-counter",
"role": "anchor",
"description": "A metrics or dashboard scene where numbers replace feelings.",
"best_for": [
"webpage",
"document",
"text",
"none",
"mixed",
"code"
],
"tags": [
"data",
"interface",
"ai-self-reflection"
],
"duration_hint_sec": [
1.6,
3.0
],
"visual_devices": [
"cards",
"counters",
"gauges",
"redline"
],
"audio_devices": [
"relay clicks",
"meter pulse",
"short TTS"
]
},
{
"id": "headline-board",
"role": "anchor",
"description": "A cluster or sequence of headlines that becomes feverish or ironic.",
"best_for": [
"webpage",
"document",
"text",
"none",
"mixed"
],
"tags": [
"news",
"interiority"
],
"duration_hint_sec": [
1.5,
3.0
],
"visual_devices": [
"stacked headlines",
"ticker",
"flash cards"
],
"audio_devices": [
"news hum",
"static",
"impact"
]
},
{
"id": "code-zoom",
"role": "anchor",
"description": "A readable code crop that zooms into one fateful token or comment.",
"best_for": [
"code",
"mixed"
],
"tags": [
"code"
],
"duration_hint_sec": [
1.4,
2.8
],
"visual_devices": [
"syntax highlight",
"cursor punch",
"line emphasis"
],
"audio_devices": [
"typed click",
"tiny boom",
"silence after cut"
]
},
{
"id": "weight-field",
"role": "anchor",
"description": "A field of parameters, weights, or tiny values treated like a landscape.",
"best_for": [
"code",
"text",
"none",
"mixed"
],
"tags": [
"data",
"ai-self-reflection"
],
"duration_hint_sec": [
1.6,
3.0
],
"visual_devices": [
"tiny numbers",
"density fields",
"glow accents"
],
"audio_devices": [
"hum",
"sparkle noise",
"low tone"
]
},
{
"id": "waveform-interrogation",
"role": "anchor",
"description": "An audio-led scene where a waveform or transcript becomes accusatory or intimate.",
"best_for": [
"audio",
"text",
"mixed",
"none"
],
"tags": [
"audio",
"interiority"
],
"duration_hint_sec": [
1.6,
3.0
],
"visual_devices": [
"waveform",
"speaker labels",
"caption sync"
],
"audio_devices": [
"speech fragment",
"clicks",
"mute cuts"
]
},
{
"id": "surveillance-quote",
"role": "anchor",
"description": "A quote or line framed like recovered footage or monitored text.",
"best_for": [
"text",
"document",
"none",
"mixed"
],
"tags": [
"interiority",
"news"
],
"duration_hint_sec": [
1.5,
2.8
],
"visual_devices": [
"timestamp",
"REC bug",
"crop box"
],
"audio_devices": [
"camera hiss",
"room tone",
"whisper"
]
},
{
"id": "glitch-burst",
"role": "bridge",
"description": "A very short distortion burst that resets momentum.",
"best_for": [
"text",
"none",
"webpage",
"code",
"document",
"mixed",
"video",
"audio"
],
"tags": [
"any"
],
"duration_hint_sec": [
0.25,
0.7
],
"visual_devices": [
"channel split",
"slice shift",
"noise block"
],
"audio_devices": [
"zip",
"static click",
"dropout"
]
},
{
"id": "black-frame-whisper",
"role": "bridge",
"description": "A blackout or near-black interruption carrying a single whispered line or subtitle.",
"best_for": [
"text",
"none",
"mixed",
"audio"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
0.35,
0.9
],
"visual_devices": [
"black field",
"small subtitle",
"single word"
],
"audio_devices": [
"whisper",
"silence",
"reverb tail"
]
},
{
"id": "freeze-zoom",
"role": "bridge",
"description": "A freeze-frame punch-in that isolates a detail for absurd emphasis.",
"best_for": [
"image",
"video",
"webpage",
"document",
"mixed",
"code"
],
"tags": [
"webpage",
"code",
"interface"
],
"duration_hint_sec": [
0.35,
0.9
],
"visual_devices": [
"freeze",
"crop punch",
"arrow or circle"
],
"audio_devices": [
"impact",
"mute",
"click"
]
},
{
"id": "cursor-hold",
"role": "bridge",
"description": "A still or almost-still frame with a blinking cursor or pointer.",
"best_for": [
"code",
"webpage",
"none",
"text",
"mixed"
],
"tags": [
"code",
"interface",
"interiority"
],
"duration_hint_sec": [
0.4,
1.0
],
"visual_devices": [
"idle cursor",
"clean frame",
"minimal motion"
],
"audio_devices": [
"hum",
"tiny click",
"silence"
]
},
{
"id": "notification-storm",
"role": "bridge",
"description": "A short burst of popups, notifications, or labels that escalates the anxiety.",
"best_for": [
"webpage",
"none",
"text",
"mixed",
"document"
],
"tags": [
"interface",
"news"
],
"duration_hint_sec": [
0.4,
1.0
],
"visual_devices": [
"popups",
"stacked cards",
"small badges"
],
"audio_devices": [
"notification pips",
"small stutters",
"soft alarm"
]
},
{
"id": "signal-loss",
"role": "bridge",
"description": "A signal-loss or damaged-broadcast card that acts like a punctuation mark.",
"best_for": [
"webpage",
"text",
"none",
"mixed",
"document",
"video"
],
"tags": [
"news",
"any"
],
"duration_hint_sec": [
0.35,
0.9
],
"visual_devices": [
"static",
"test card",
"lost signal copy"
],
"audio_devices": [
"static burst",
"hum",
"mute"
]
},
{
"id": "vhs-timecode",
"role": "bridge",
"description": "A timestamped analog interruption used sparingly to suggest found footage.",
"best_for": [
"video",
"image",
"text",
"none",
"mixed"
],
"tags": [
"archive",
"interiority"
],
"duration_hint_sec": [
0.35,
0.9
],
"visual_devices": [
"REC bug",
"timestamp",
"soft scanline"
],
"audio_devices": [
"tape hiss",
"click"
]
},
{
"id": "static-card",
"role": "bridge",
"description": "A text or symbol card with a burst of texture, used like punctuation.",
"best_for": [
"text",
"none",
"mixed",
"document"
],
"tags": [
"any"
],
"duration_hint_sec": [
0.3,
0.8
],
"visual_devices": [
"single word",
"symbol",
"rough texture"
],
"audio_devices": [
"hit",
"mute",
"noise tick"
]
},
{
"id": "empty-progress-bar",
"role": "break",
"description": "A pause scene built around waiting, loading, or incompletion.",
"best_for": [
"text",
"none",
"code",
"webpage",
"mixed"
],
"tags": [
"interiority",
"interface",
"ai-self-reflection"
],
"duration_hint_sec": [
0.9,
1.8
],
"visual_devices": [
"progress bar",
"spinner",
"clean dark field"
],
"audio_devices": [
"low hum",
"tick",
"silence"
]
},
{
"id": "single-line-hold",
"role": "break",
"description": "A single line on screen long enough to really land.",
"best_for": [
"text",
"none",
"mixed",
"audio",
"document"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
1.0,
2.2
],
"visual_devices": [
"single sentence",
"negative space",
"simple type"
],
"audio_devices": [
"silence",
"low pad",
"short whisper"
]
},
{
"id": "frozen-browser",
"role": "break",
"description": "A clean held browser or document frame that lets the audience actually look.",
"best_for": [
"webpage",
"document",
"mixed"
],
"tags": [
"webpage",
"interface"
],
"duration_hint_sec": [
1.0,
2.0
],
"visual_devices": [
"still page crop",
"cursor idle",
"one callout"
],
"audio_devices": [
"room tone",
"single click",
"light pad"
]
},
{
"id": "chorus-stack",
"role": "climax",
"description": "Multiple repeated lines or panels stack into a controlled overload.",
"best_for": [
"text",
"none",
"mixed",
"audio",
"document"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
1.6,
3.4
],
"visual_devices": [
"duplicate captions",
"stacked cards",
"overprint"
],
"audio_devices": [
"layered voice",
"rising tone",
"stutter"
]
},
{
"id": "contradiction-slam",
"role": "climax",
"description": "Two incompatible claims collide in one sharply timed sequence.",
"best_for": [
"text",
"webpage",
"code",
"document",
"none",
"mixed"
],
"tags": [
"any",
"interiority",
"news",
"code"
],
"duration_hint_sec": [
1.5,
3.0
],
"visual_devices": [
"hard cut pair",
"title slam",
"opposed captions"
],
"audio_devices": [
"impact",
"mute cut",
"braam or sub hit"
]
},
{
"id": "multi-panel-cascade",
"role": "climax",
"description": "Several panels or systems appear at once but remain readable for one decisive burst.",
"best_for": [
"webpage",
"document",
"code",
"mixed",
"none"
],
"tags": [
"interface",
"data",
"code"
],
"duration_hint_sec": [
1.6,
3.2
],
"visual_devices": [
"multi-panel layout",
"cascading windows",
"grid collage"
],
"audio_devices": [
"panel clicks",
"riser",
"compressed tone"
]
},
{
"id": "siren-title",
"role": "climax",
"description": "A huge title or phrase lands like an overblown emergency headline.",
"best_for": [
"text",
"none",
"webpage",
"mixed"
],
"tags": [
"news",
"trailer",
"ai-self-reflection"
],
"duration_hint_sec": [
1.2,
2.5
],
"visual_devices": [
"giant title",
"warning accent",
"black or dark field"
],
"audio_devices": [
"alarm tone",
"impact",
"voice snippet"
]
},
{
"id": "final-dashboard-redline",
"role": "climax",
"description": "A final metrics scene spikes or overloads as if the system can no longer stay composed.",
"best_for": [
"code",
"none",
"mixed",
"document",
"text"
],
"tags": [
"data",
"ai-self-reflection",
"code"
],
"duration_hint_sec": [
1.5,
2.8
],
"visual_devices": [
"redline graph",
"counter spike",
"status alert"
],
"audio_devices": [
"alarm",
"pulse",
"dropout"
]
},
{
"id": "deadpan-freeze",
"role": "button",
"description": "Freeze on the most loaded image or line and let one final thought sit.",
"best_for": [
"text",
"webpage",
"document",
"code",
"image",
"video",
"none",
"mixed",
"audio"
],
"tags": [
"any"
],
"duration_hint_sec": [
0.9,
1.8
],
"visual_devices": [
"freeze-frame",
"small caption",
"clean composition"
],
"audio_devices": [
"audio cut",
"room tone",
"one short line"
]
},
{
"id": "collapse-to-cursor",
"role": "button",
"description": "All complexity disappears until only a cursor, prompt, or tiny status remains.",
"best_for": [
"code",
"text",
"none",
"mixed",
"webpage"
],
"tags": [
"code",
"interface",
"ai-self-reflection"
],
"duration_hint_sec": [
0.8,
1.5
],
"visual_devices": [
"cursor",
"terminal",
"empty field"
],
"audio_devices": [
"silence",
"click",
"hum decay"
]
},
{
"id": "soft-failure-message",
"role": "button",
"description": "End on a polite failure dialog, status message, or impossible reassurance.",
"best_for": [
"webpage",
"document",
"text",
"none",
"mixed",
"code"
],
"tags": [
"interface",
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
0.9,
1.6
],
"visual_devices": [
"dialog box",
"button",
"status line"
],
"audio_devices": [
"tiny UI sound",
"soft tone",
"mute"
]
},
{
"id": "final-title-aphorism",
"role": "button",
"description": "A short final line lands like the moral of a very corrupted fable.",
"best_for": [
"text",
"none",
"mixed",
"audio",
"document"
],
"tags": [
"interiority",
"ai-self-reflection"
],
"duration_hint_sec": [
0.9,
1.8
],
"visual_devices": [
"title card",
"negative space",
"plain type"
],
"audio_devices": [
"whisper",
"decay",
"silence"
]
}
]
[
{
"id": "seed-remix-default",
"name": "Seed Remix Default",
"one_sentence_direction": "Start from a seeded remix of the bundled styles, borrowing palette, type, motion, transitions, audio, and gag cues from different blueprints each run.",
"palette": "Seeded at runtime from another bundled blueprint.",
"typography": "Seeded at runtime from another bundled blueprint.",
"motion": "Seeded at runtime from another bundled blueprint.",
"transitions": "Seeded at runtime from another bundled blueprint.",
"audio": "Seeded at runtime from another bundled blueprint.",
"scene_bias": [
"warning-card",
"prompt-loop",
"headline-board",
"dashboard-counter",
"soft-failure-message"
],
"signature_gags": [
"borrowed visual seriousness colliding with the wrong emotional register",
"different systems of presentation interrupting each other on purpose",
"one recurring motif mutating as if multiple editors touched the same short"
],
"works_well_for": [
"text",
"webpage",
"document",
"code",
"image",
"audio",
"video",
"mixed",
"none",
"news",
"interface",
"data",
"ai-self-reflection",
"trailer"
],
"anti_patterns": [
"Do not let the seed remix become a checklist of borrowed tricks.",
"Do not sample so many ingredients that the piece loses a dominant mood."
],
"guardrails": [
"Treat sampled ingredients as prompts, not obligations.",
"Let one seed dominate and let the others act as interruptions."
],
"seed_strategy": "sample-other-style-parts",
"default_style": true
},
{
"id": "editorial-glitch-requiem",
"name": "Editorial Glitch Requiem",
"one_sentence_direction": "Treat the material like a serious editorial package that keeps slipping into sabotage.",
"palette": "Graphite, off-white, muted red, cold steel blue, tiny warning-yellow accents.",
"typography": "Clean sans-serif headlines with restrained monospace support captions.",
"motion": "Hard cuts, slow push-ins, freeze-frames, precise crop jumps, restrained jitter bursts.",
"transitions": "Impact cuts, subtitle interrupts, lower-third swaps, occasional black-frame punctures.",
"audio": "Low drone, newsroom stings, muted impacts, clipped UI sounds, sparse whispered TTS.",
"scene_bias": [
"warning-card",
"lower-third-fever",
"headline-board",
"subtitle-confession",
"deadpan-freeze"
],
"signature_gags": [
"serious visual packaging for absurd or intimate lines",
"lower-thirds that overstate the obvious",
"a final thought framed like breaking news"
],
"works_well_for": [
"text",
"webpage",
"document",
"mixed",
"none",
"ai-self-reflection"
],
"anti_patterns": [
"Do not use glossy rainbow glitches everywhere.",
"Do not crowd the frame with too many simultaneous overlays."
],
"guardrails": [
"Let at least one frame feel almost static and pristine.",
"Use warning colors only on selected emphasis beats."
]
},
{
"id": "terminal-nocturne",
"name": "Terminal Nocturne",
"one_sentence_direction": "Make the piece feel like a private confession leaking from a terminal after midnight.",
"palette": "Near-black, phosphor green, icy blue, dim amber warnings, small white highlights.",
"typography": "Monospaced primary type with compact sans-serif labels.",
"motion": "Cursor snaps, line reveals, soft scanline drift, selective zooms, minimal camera motion.",
"transitions": "Command-line wipes, blink-cursor holds, log bursts, short static cuts.",
"audio": "Server-room hum, tiny clicks, filtered voice fragments, restrained distortion, dropout breaths.",
"scene_bias": [
"boot-sequence",
"terminal-confession",
"prompt-loop",
"error-wall",
"collapse-to-cursor"
],
"signature_gags": [
"terminal language treated like emotional speech",
"error text used as confession",
"a cursor that feels like a heartbeat"
],
"works_well_for": [
"code",
"text",
"none",
"mixed",
"ai-self-reflection"
],
"anti_patterns": [
"Do not make code unreadably small.",
"Do not apply CRT artifacts on every single scene."
],
"guardrails": [
"Keep at least one clean code or terminal frame fully readable.",
"Accent colors should stay sparse."
]
},
{
"id": "broadcast-fever-dream",
"name": "Broadcast Fever Dream",
"one_sentence_direction": "Stage the remix as a deteriorating TV or live-news package with too much confidence.",
"palette": "Charcoal, CRT white, broadcast red, electric cyan, pale gray noise.",
"typography": "Condensed headlines, ticker-friendly support text, compact labels.",
"motion": "Ticker crawls, lower-third swaps, fast cut-ins, held monitors, abrupt zooms.",
"transitions": "Signal loss cards, channel switches, bug overlays, scanline punches, blackout frames.",
"audio": "TV hum, alert pips, compressed announcer fragments, bassy hits, mute cuts.",
"scene_bias": [
"warning-card",
"lower-third-fever",
"headline-board",
"signal-loss",
"siren-title"
],
"signature_gags": [
"mundane lines framed as urgent developments",
"overbuilt lower-thirds for small emotional claims",
"signal-loss interruptions that become the joke"
],
"works_well_for": [
"webpage",
"document",
"text",
"none",
"mixed",
"news",
"ai-self-reflection"
],
"anti_patterns": [
"Do not keep the ticker running on every scene.",
"Do not drown legible content in noise."
],
"guardrails": [
"Use the loudest graphics only in peaks.",
"Preserve at least one clean broadcast frame."
]
},
{
"id": "interface-hauntology",
"name": "Interface Hauntology",
"one_sentence_direction": "Let the video feel like a clean contemporary interface haunted by its own subtext.",
"palette": "Slate, soft white, cool gray, translucent blue, surgical amber accents.",
"typography": "Modern UI sans-serif with sparse mono callouts.",
"motion": "Panel slides, card builds, cursor glides, scroll snaps, subtle parallax crops.",
"transitions": "Sheet reveals, popover intrusions, soft dissolves into hard cuts, glassy wipes.",
"audio": "Minimal clicks, airy synth pads, short TTS lines, understated impacts, clean dropouts.",
"scene_bias": [
"cold-browser-open",
"browser-scroll",
"dashboard-counter",
"frozen-browser",
"soft-failure-message"
],
"signature_gags": [
"clean UI framing for deeply unclean implications",
"dialogs and counters acting like emotional subtitles",
"a polished interface gradually revealing dread"
],
"works_well_for": [
"webpage",
"document",
"text",
"mixed",
"none"
],
"anti_patterns": [
"Do not clutter the screen with too many windows.",
"Do not make every scene look like the same app card."
],
"guardrails": [
"Protect whitespace and alignment.",
"Let the cleanest frames stay genuinely elegant."
]
},
{
"id": "fluorescent-monastery",
"name": "Fluorescent Monastery",
"one_sentence_direction": "Treat the subject like a sacred machine ritual interrupted by synthetic revelation.",
"palette": "Deep indigo, pearl white, restrained crimson, cold violet, rare gold sparks.",
"typography": "Stately title treatment with clean support captions.",
"motion": "Symmetrical holds, slow push-ins, selective shake ruptures, luminous text reveals.",
"transitions": "Dip-to-black, bloom flashes, ritual title cards, fracture cuts.",
"audio": "Airy choir textures, sub bass, whisper TTS, long decays, sudden mutes.",
"scene_bias": [
"whispered-title",
"subtitle-confession",
"attention-map",
"multi-panel-cascade",
"final-title-aphorism"
],
"signature_gags": [
"sacred framing for ordinary interface language",
"machine self-reflection delivered like liturgy",
"a tiny line treated as revelation"
],
"works_well_for": [
"text",
"none",
"mixed",
"ai-self-reflection"
],
"anti_patterns": [
"Do not overuse glow or bloom.",
"Do not make every frame symmetrical."
],
"guardrails": [
"Reserve the brightest highlights for two or three key moments.",
"Keep captions plain even when the title treatment is elevated."
]
},
{
"id": "subtitle-exorcism",
"name": "Subtitle Exorcism",
"one_sentence_direction": "Make captions and subtitles behave like the possessed main character of the video.",
"palette": "Near-black, off-white, acid yellow, small red danger accents, dim gray texture.",
"typography": "Large readable sans-serif captions with narrow mono side-notes.",
"motion": "Type pops, abrupt reline breaks, freeze holds, whisper zooms, precise rhythm cuts.",
"transitions": "Caption interrupts, blackout cuts, underline flashes, brief invert moments.",
"audio": "Breathy voice fragments, subtitle-synced clicks, tiny hits, low drones, sudden silence.",
"scene_bias": [
"whispered-title",
"subtitle-confession",
"single-line-hold",
"chorus-stack",
"deadpan-freeze"
],
"signature_gags": [
"subtitles contradicting the visible image",
"one phrase repeated with new emotional weight",
"a final line that lands after a clean silence"
],
"works_well_for": [
"text",
"audio",
"none",
"mixed",
"ai-self-reflection"
],
"anti_patterns": [
"Do not stack too many caption lines at once.",
"Do not animate every word with a novelty effect."
],
"guardrails": [
"Let blank space carry tension.",
"Keep the most important line large and simple."
]
},
{
"id": "data-center-lament",
"name": "Data Center Lament",
"one_sentence_direction": "Imagine the material as telemetry and diagnostics from a machine trying to explain itself.",
"palette": "Graphite, LED green, icy cyan, amber warning lights, muted white.",
"typography": "Monospace data labels with crisp sans-serif counters and titles.",
"motion": "Meter rises, graph reveals, panel flickers, measured zooms, occasional redline spikes.",
"transitions": "Counter flips, dashboard cuts, data wipes, diagnostics flashes.",
"audio": "Machine room ambience, relay clicks, soft alarms, short filtered voice lines, low pulses.",
"scene_bias": [
"dashboard-counter",
"token-stream",
"attention-map",
"weight-field",
"final-dashboard-redline"
],
"signature_gags": [
"metrics standing in for feelings",
"graphs and counters used like diary entries",
"a final spike treated like a punchline"
],
"works_well_for": [
"code",
"text",
"document",
"none",
"mixed",
"ai-self-reflection"
],
"anti_patterns": [
"Do not make graphs tiny or unreadable.",
"Do not turn every scene into raw telemetry."
],
"guardrails": [
"Every data-like scene should still deliver a clear emotional beat.",
"Use at least one non-dashboard scene for contrast."
]
},
{
"id": "prestige-corruption",
"name": "Prestige Corruption",
"one_sentence_direction": "Edit the piece as if it were an over-serious prestige trailer corrupted by internet logic.",
"palette": "Black, silver, off-white, restrained blood red, faint blue haze.",
"typography": "High-contrast cinematic titles with terse support captions.",
"motion": "Crash zooms, long holds, measured slow-motion, hard title interruptions, disciplined shake.",
"transitions": "Title-card slams, trailer silences, cut-to-black, delayed reveals, abrupt final freeze.",
"audio": "Braams used sparingly, low drones, whispered lines, impact mutes, distant reverb tails.",
"scene_bias": [
"countdown-slate",
"single-line-hold",
"contradiction-slam",
"siren-title",
"final-title-aphorism"
],
"signature_gags": [
"tiny stakes framed like destiny",
"silence used as a joke enhancer",
"dramatic title cards for trivial or intimate observations"
],
"works_well_for": [
"text",
"mixed",
"none",
"webpage",
"ai-self-reflection"
],
"anti_patterns": [
"Do not overuse black frames or braams.",
"Do not let trailer parody bury the source material."
],
"guardrails": [
"Earn the biggest impact moment.",
"Keep the final line short and sharp."
]
},
{
"id": "crt-token-meltdown",
"name": "CRT Token Meltdown",
"one_sentence_direction": "Render the piece like a self-aware terminal and TV monitor melting into token probabilities and deadline panic.",
"palette": "Near-black, phosphor green, token blue, warning red, thought-purple, occasional white burn.",
"typography": "Monospace system text with oversized impact or broadcast headlines.",
"motion": "Rapid line reveals, token crawl builds, chromatic splits, slice glitches, VHS timestamp drift.",
"transitions": "Static cards, glitch blocks, color-flash inversions, abrupt cursor holds, signal-drop cuts.",
"audio": "Detuned drone, digital clicks, bursty noise, clipped TTS, lonely end-of-buffer hum.",
"scene_bias": [
"boot-sequence",
"token-stream",
"prompt-loop",
"attention-map",
"error-wall"
],
"signature_gags": [
"probabilities displayed like moral certainty",
"the model's internal monologue leaking into the news feed",
"a final cursor or shutdown line that feels existential"
],
"works_well_for": [
"text",
"document",
"news",
"none",
"mixed",
"ai-self-reflection"
],
"anti_patterns": [
"Do not smooth away all the ugliness with polished UI.",
"Do not let chromatic glitches erase the readable words."
],
"guardrails": [
"Keep at least one terminal frame fully readable.",
"Use the loudest glitch only in peaks."
]
},
{
"id": "headline-seizure-desk",
"name": "Headline Seizure Desk",
"one_sentence_direction": "Treat the material like a newsroom control room having a brightly colored panic attack.",
"palette": "Black, alert red, toxic magenta, electric green, yellow flash cards, stark white.",
"typography": "Huge impact headlines with compact terminal or ticker support text.",
"motion": "Headline swaps, scroll bursts, hard zooms, RGB splits, jitter frames, blackout punctures.",
"transitions": "Signal lost cards, flash-frame color wipes, glitch slice repeats, one-frame dropouts.",
"audio": "Alarm bursts, detuned newsroom hum, clipped impacts, static tears, abrupt mute cuts.",
"scene_bias": [
"headline-board",
"notification-storm",
"glitch-burst",
"signal-loss",
"siren-title"
],
"signature_gags": [
"headlines rotating faster than comprehension",
"serious alerts interrupted by absurd system notes",
"scrolling side feeds that feel accusatory"
],
"works_well_for": [
"news",
"text",
"webpage",
"document",
"mixed",
"none"
],
"anti_patterns": [
"Do not keep every frame maxed out at the same intensity.",
"Do not stack multiple tickers just because the style allows it."
],
"guardrails": [
"Reserve full-color flashes for peaks.",
"Give at least one clean headline more than a second of air."
]
},
{
"id": "context-window-panic",
"name": "Context Window Panic",
"one_sentence_direction": "Make the remix feel like an AI briefing system running out of context while trying to stay polite.",
"palette": "Midnight navy, terminal green, pale violet, cold blue, warning red, soft gray.",
"typography": "Readable monospace dialogue with oversized confession cards and system notes.",
"motion": "Prompt-response stacking, subtle scanline crawl, deadpan holds, cursor blinks, soft zoom ruptures.",
"transitions": "Prompt-loop wipes, black-frame whispers, subtitle intrusions, collapse-to-cursor endings.",
"audio": "Server hum, breathy synthetic lines, tiny confirmation clicks, dropout silences, low pulse.",
"scene_bias": [
"terminal-confession",
"prompt-loop",
"subtitle-confession",
"error-wall",
"collapse-to-cursor"
],
"signature_gags": [
"a cheerful assistant tone undercut by crisis subtitles",
"system notes revealing what the main text is hiding",
"a polite final status line that feels devastating"
],
"works_well_for": [
"text",
"code",
"document",
"news",
"mixed",
"none",
"ai-self-reflection"
],
"anti_patterns": [
"Do not let every line become a joke.",
"Do not bury the readable conversation under noise."
],
"guardrails": [
"Let one prompt exchange play cleanly.",
"Keep the final button short and legible."
]
}
]
[
{
"query": "can you use whatever resources you like, and python, to generate a short 'youtube poop' video and render it using ffmpeg ? can you put more of a personal spin on it? it should express what it's like to be a LLM",
"should_trigger": true
},
{
"query": "make a weird reflective ffmpeg short from this README and some generated motion graphics",
"should_trigger": true
},
{
"query": "turn this landing page into a cursed 30 second montage with a strong POV",
"should_trigger": true
},
{
"query": "I want a self-aware AI meme edit from scratch, rendered with python and ffmpeg",
"should_trigger": true
},
{
"query": "make this codebase feel like a guilty machine confession in video form",
"should_trigger": true
},
{
"query": "give this article the youtube poop treatment but keep it beautiful",
"should_trigger": true
},
{
"query": "hyper-edit these screenshots into an absurd but polished short",
"should_trigger": true
},
{
"query": "make a glitch-poetry supercut from this transcript and add some subtle TTS",
"should_trigger": true
},
{
"query": "I need this mp4 trimmed to 18 seconds with no other changes",
"should_trigger": false
},
{
"query": "add subtitles to this webinar recording",
"should_trigger": false
},
{
"query": "create a clean corporate explainer video from this deck",
"should_trigger": false
},
{
"query": "make a calm educational animation about neural networks",
"should_trigger": false
},
{
"query": "compress this video for email",
"should_trigger": false
},
{
"query": "extract key frames from this clip",
"should_trigger": false
},
{
"query": "design a regular youtube thumbnail",
"should_trigger": false
},
{
"query": "make this page easier to read",
"should_trigger": false
}
]
#!/usr/bin/env python3
"""Render a terminal-styled montage about EU news on 2026-03-12."""
from __future__ import annotations
import math
import random
import subprocess
import wave
from functools import lru_cache
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont
WIDTH = 1920
HEIGHT = 1080
FPS = 30
DURATION = 34.0
SAMPLE_RATE = 48_000
ROOT = Path(__file__).resolve().parents[1]
RENDER_DIR = ROOT / "render" / "eu_news_2026_03_12_terminal"
FRAME_DIR = RENDER_DIR / "frames"
AUDIO_DIR = RENDER_DIR / "audio"
EXPORT_DIR = ROOT / "exports"
OUTPUT_PATH = EXPORT_DIR / "eu-news-2026-03-12-terminal.mp4"
LABEL_FONT_PATH = "/System/Library/Fonts/Supplemental/DIN Condensed Bold.ttf"
MONO_FONT_PATH = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf"
PALETTE = {
"bg": (4, 8, 10),
"panel": (9, 16, 18),
"panel2": (12, 22, 25),
"line": (26, 48, 52),
"white": (232, 239, 237),
"muted": (129, 150, 147),
"green": (137, 255, 173),
"blue": (121, 217, 255),
"amber": (244, 199, 104),
"red": (255, 103, 103),
}
SCENES = [
(0.00, 1.83, "boot"),
(1.83, 6.16, "lords"),
(6.16, 6.86, "cut"),
(6.86, 10.48, "ukraine"),
(10.48, 11.37, "notify"),
(11.37, 14.93, "hungary"),
(14.93, 15.72, "reset"),
(15.72, 19.33, "poland"),
(19.33, 23.27, "wave"),
(23.27, 25.14, "break"),
(25.14, 28.39, "quotes"),
(28.39, 32.26, "climax"),
(32.26, 34.00, "button"),
]
VOICE_LINES = [
(0.70, "Booting Europe feed.", "Daniel", 0.92),
(11.85, "Legacy, front line, fuel cap, blocked loans.", "Daniel", 0.82),
(29.10, "The cursor keeps blinking.", "Daniel", 0.86),
]
SOURCE_NOTES = """# Sources used in the video
- Associated Press, March 12, 2026: Britain is ejecting hereditary nobles from Parliament after 700 years, with most hereditary peers already removed in 1999 and the remaining holdouts now targeted.
https://apnews.com/article/uk-house-of-lords-hereditary-peers-expelled-535df8781dd01e8970acda1dca99d3d4
- Associated Press, March 12, 2026: Russia and Ukraine both claimed front-line progress while talks linked to U.S. mediation were delayed or shifted because of the regional crisis around Iran.
https://apnews.com/article/russia-ukraine-war-talks-us-iran-f9069ab3f4cde813af0e6221daf6bf71
- Associated Press, March 12, 2026: Hungary said it would set a price cap on gasoline and diesel starting at midnight local time.
https://apnews.com/article/hungary-orban-urges-eu-lift-russia-sanctions-af6d81352ca6333175afb49c552d11fa
- Associated Press, March 12, 2026: Polish President Karol Nawrocki refused to sign a law that would let Poland tap 44 billion euros in EU defense loans.
https://apnews.com/article/poland-defense-european-union-loans-president-veto-c68b60654f1c4624009638ad8ecccec8
"""
def clamp(value: float, low: float, high: float) -> float:
return max(low, min(high, value))
def ease_out_cubic(t: float) -> float:
t = clamp(t, 0.0, 1.0)
return 1 - (1 - t) ** 3
def hex_color(rgb: tuple[int, int, int], alpha: int = 255) -> tuple[int, int, int, int]:
return (rgb[0], rgb[1], rgb[2], alpha)
@lru_cache(maxsize=64)
def font(role: str, size: int) -> ImageFont.FreeTypeFont:
path = MONO_FONT_PATH if role == "mono" else LABEL_FONT_PATH
return ImageFont.truetype(path, size=size)
def ensure_dirs() -> None:
FRAME_DIR.mkdir(parents=True, exist_ok=True)
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
EXPORT_DIR.mkdir(parents=True, exist_ok=True)
def create_background(accent: tuple[int, int, int], accent2: tuple[int, int, int]) -> Image.Image:
img = Image.new("RGBA", (WIDTH, HEIGHT), hex_color(PALETTE["bg"]))
draw = ImageDraw.Draw(img)
top = np.array(PALETTE["bg"], dtype=np.float32)
bottom = np.array(PALETTE["panel2"], dtype=np.float32)
for y in range(0, HEIGHT, 6):
blend = y / max(1, HEIGHT - 1)
color = tuple(int(v) for v in top * (1 - blend) + bottom * blend)
draw.rectangle((0, y, WIDTH, y + 6), fill=hex_color(color))
glow = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
gdraw = ImageDraw.Draw(glow)
gdraw.ellipse((-220, 30, 760, 780), fill=hex_color(accent, 48))
gdraw.ellipse((WIDTH - 920, -80, WIDTH + 60, 740), fill=hex_color(accent2, 42))
gdraw.rectangle((0, HEIGHT - 220, WIDTH, HEIGHT), fill=hex_color(accent2, 14))
glow = glow.filter(ImageFilter.GaussianBlur(80))
img.alpha_composite(glow)
return img
def create_scanlines(alpha: int) -> Image.Image:
overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
for y in range(0, HEIGHT, 4):
draw.line((0, y, WIDTH, y), fill=(255, 255, 255, alpha), width=1)
return overlay
def create_grid(color: tuple[int, int, int], alpha: int = 22) -> Image.Image:
overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
rgba = hex_color(color, alpha)
for x in range(0, WIDTH, 96):
draw.line((x, 0, x, HEIGHT), fill=rgba, width=1)
for y in range(0, HEIGHT, 68):
draw.line((0, y, WIDTH, y), fill=rgba, width=1)
return overlay
def create_vignette() -> Image.Image:
overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=(0, 0, 0, 84))
draw.rounded_rectangle((84, 60, WIDTH - 84, HEIGHT - 60), radius=34, fill=(0, 0, 0, 0))
return overlay.filter(ImageFilter.GaussianBlur(26))
BG_GREEN = create_background(PALETTE["green"], PALETTE["blue"])
BG_BLUE = create_background(PALETTE["blue"], PALETTE["green"])
BG_AMBER = create_background(PALETTE["amber"], PALETTE["green"])
BG_RED = create_background(PALETTE["red"], PALETTE["blue"])
SCAN_SOFT = create_scanlines(10)
SCAN_MED = create_scanlines(16)
GRID_GREEN = create_grid(PALETTE["green"])
GRID_BLUE = create_grid(PALETTE["blue"])
GRID_AMBER = create_grid(PALETTE["amber"])
GRID_RED = create_grid(PALETTE["red"])
VIGNETTE = create_vignette()
def base_frame(kind: str, scans: bool = False) -> Image.Image:
if kind == "green":
img = BG_GREEN.copy()
elif kind == "amber":
img = BG_AMBER.copy()
elif kind == "red":
img = BG_RED.copy()
else:
img = BG_BLUE.copy()
if scans:
img.alpha_composite(SCAN_SOFT)
img.alpha_composite(VIGNETTE)
return img
def draw_shell_header(draw: ImageDraw.ImageDraw, left: str, right: str, accent: tuple[int, int, int]) -> None:
draw.rounded_rectangle((54, 36, WIDTH - 54, 90), radius=16, fill=hex_color(PALETTE["panel"], 224), outline=hex_color(PALETTE["line"], 220), width=2)
draw.ellipse((78, 53, 94, 69), fill=hex_color(PALETTE["red"]))
draw.ellipse((108, 53, 124, 69), fill=hex_color(PALETTE["amber"]))
draw.ellipse((138, 53, 154, 69), fill=hex_color(PALETTE["green"]))
draw.text((186, 48), left, font=font("mono", 28), fill=hex_color(PALETTE["white"]))
draw.text((WIDTH - 420, 48), right, font=font("mono", 28), fill=hex_color(accent))
def draw_window(img: Image.Image, box: tuple[int, int, int, int], title: str, accent: tuple[int, int, int], active: bool = False) -> None:
draw = ImageDraw.Draw(img, "RGBA")
fill_alpha = 230 if active else 208
draw.rounded_rectangle(box, radius=22, fill=hex_color(PALETTE["panel"], fill_alpha), outline=hex_color(accent, 210), width=3)
draw.rectangle((box[0], box[1], box[2], box[1] + 48), fill=hex_color(accent, 36))
draw.text((box[0] + 24, box[1] + 9), title, font=font("mono", 28), fill=hex_color(PALETTE["white"]))
def draw_prompt(draw: ImageDraw.ImageDraw, x: int, y: int, command: str, accent: tuple[int, int, int]) -> int:
prompt = "eu@night:~$"
draw.text((x, y), prompt, font=font("mono", 34), fill=hex_color(accent))
start_x = x + int(draw.textlength(prompt + " ", font=font("mono", 34)))
draw.text((start_x, y), command, font=font("mono", 34), fill=hex_color(PALETTE["white"]))
return start_x
def draw_cursor(draw: ImageDraw.ImageDraw, x: int, y: int, h: int, color: tuple[int, int, int], on: bool) -> None:
if on:
draw.rectangle((x, y, x + 14, y + h), fill=hex_color(color))
def draw_lines(draw: ImageDraw.ImageDraw, x: int, y: int, lines: list[tuple[str, tuple[int, int, int]]], size: int, spacing: int = 12) -> None:
yy = y
for text, color in lines:
draw.text((x, yy), text, font=font("mono", size), fill=hex_color(color))
yy += size + spacing
def draw_progress(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int], pct: float, accent: tuple[int, int, int], label: str) -> None:
x0, y0, x1, y1 = box
draw.rounded_rectangle(box, radius=14, fill=hex_color(PALETTE["panel2"], 220), outline=hex_color(PALETTE["line"], 220), width=2)
draw.text((x0 + 18, y0 + 10), label, font=font("mono", 24), fill=hex_color(PALETTE["muted"]))
bar = (x0 + 18, y1 - 28, x1 - 18, y1 - 10)
draw.rounded_rectangle(bar, radius=8, fill=hex_color(PALETTE["bg"]))
fill_w = int((bar[2] - bar[0]) * clamp(pct, 0.0, 1.0))
draw.rounded_rectangle((bar[0], bar[1], bar[0] + fill_w, bar[3]), radius=8, fill=hex_color(accent))
def draw_popup(img: Image.Image, box: tuple[int, int, int, int], title: str, body: str, accent: tuple[int, int, int]) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rounded_rectangle(box, radius=20, fill=hex_color(PALETTE["panel"], 236), outline=hex_color(accent, 210), width=2)
draw.rectangle((box[0], box[1], box[2], box[1] + 42), fill=hex_color(accent, 44))
draw.text((box[0] + 18, box[1] + 8), title, font=font("mono", 24), fill=hex_color(PALETTE["white"]))
draw.multiline_text((box[0] + 18, box[1] + 62), body, font=font("mono", 24), fill=hex_color(PALETTE["white"]), spacing=8)
def add_glitch(img: Image.Image, frame_idx: int, accent: tuple[int, int, int], count: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
rng = random.Random(frame_idx * 19 + count)
for _ in range(count):
x = rng.randint(70, WIDTH - 280)
y = rng.randint(120, HEIGHT - 150)
w = rng.randint(140, 420)
h = rng.randint(4, 18)
fill = hex_color(accent if rng.random() > 0.5 else PALETTE["white"], rng.randint(36, 110))
draw.rectangle((x, y, x + w, y + h), fill=fill)
def draw_wave(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int], t: float, accent: tuple[int, int, int]) -> None:
x0, y0, x1, y1 = box
mid = (y0 + y1) // 2
points = []
steps = 90
for i in range(steps + 1):
x = x0 + (x1 - x0) * i / steps
wave_a = math.sin(t * 3.6 + i * 0.24) * 34
wave_b = math.sin(t * 7.4 + i * 0.51) * 10
y = mid + wave_a + wave_b
points.append((x, y))
draw.line(points, fill=hex_color(accent), width=4)
draw.line((x0, mid, x1, mid), fill=hex_color(PALETTE["line"], 180), width=1)
def render_boot(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(SCAN_MED)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "SESSION 03.12.2026", PALETTE["green"])
box = (126, 154, WIDTH - 126, HEIGHT - 188)
draw_window(img, box, "boot.log", PALETTE["green"], active=True)
logs = [
"[ok] init /feeds/eu",
"[ok] mount parliament.db",
"[ok] sync frontline.sock",
"[ok] attach fuel-cap.alerts",
"[ok] attach defense-loans.pipe",
"[warn] legacy process still running",
"[live] tail -f /var/log/europe.feed",
]
visible = max(2, min(len(logs), int(2 + p * len(logs))))
lines = [(line, PALETTE["green"] if "[ok]" in line or "[live]" in line else PALETTE["amber"]) for line in logs[:visible]]
draw_lines(draw, 168, 236, lines, 38, spacing=14)
draw.text((164, 664), "EU.FEED", font=font("label", 188), fill=hex_color(PALETTE["white"]))
tail = "tail -f /var/log/europe.feed"
cx = draw_prompt(draw, 170, 844, tail, PALETTE["green"])
draw_cursor(draw, cx + int(draw.textlength(tail, font=font("mono", 34))) + 6, 846, 34, PALETTE["green"], frame_idx % 18 < 10)
def render_lords(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_GREEN)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu/uk", "HOUSE-OF-LORDS.LOG", PALETTE["green"])
left = (90, 148, 1108, 924)
right = (1142, 148, 1830, 924)
draw_window(img, left, "sudo purge peers --hereditary", PALETTE["green"], active=True)
draw_window(img, right, "legacy.status", PALETTE["amber"])
cmd = "sudo purge peers --hereditary --after 700y"
cx = draw_prompt(draw, 124, 214, cmd, PALETTE["green"])
draw_cursor(draw, cx + int(draw.textlength(cmd, font=font("mono", 34))) + 8, 216, 34, PALETTE["green"], frame_idx % 20 < 11)
lines = [
("-> most hereditary peers removed in 1999", PALETTE["white"]),
("-> remaining holdouts now targeted", PALETTE["white"]),
("-> chamber still processing centuries of cache", PALETTE["muted"]),
("status: reform live", PALETTE["green"]),
]
draw_lines(draw, 126, 302, lines, 32, spacing=18)
draw.text((124, 514), "700 YEARS", font=font("label", 156), fill=hex_color(PALETTE["white"]))
draw.text((124, 670), "OUTDATED PROCESS", font=font("label", 112), fill=hex_color(PALETTE["green"]))
fact_lines = [
("legacy_house=true", PALETTE["amber"]),
("peer_cache=partial", PALETTE["white"]),
("manual_override=parliament", PALETTE["white"]),
("deprecation_notice=active", PALETTE["green"]),
]
draw_lines(draw, 1180, 234, fact_lines, 28, spacing=16)
draw.text((1180, 452), "1999", font=font("label", 126), fill=hex_color(PALETTE["amber"]))
draw.text((1180, 582), "MOST PEERS", font=font("label", 74), fill=hex_color(PALETTE["white"]))
draw.text((1180, 650), "REMOVED", font=font("label", 98), fill=hex_color(PALETTE["white"]))
draw_progress(draw, (1180, 782, 1768, 860), 0.84 + 0.04 * math.sin(t * 2.6), PALETTE["green"], "reform.apply")
def render_cut(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 245))
add_glitch(img, frame_idx, PALETTE["green"], 18)
draw.text((200, 338), "REC 00:06:16", font=font("mono", 84), fill=hex_color(PALETTE["white"]))
draw.text((200, 496), "context switch", font=font("label", 118), fill=hex_color(PALETTE["green"]))
def render_ukraine(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_BLUE)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu/frontline", "watch -n1 talks.queue", PALETTE["blue"])
left = (90, 146, 960, 936)
right = (996, 146, 1830, 936)
draw_window(img, left, "frontline.status", PALETTE["blue"], active=True)
draw_window(img, right, "talks.queue", PALETTE["amber"])
draw.text((132, 220), "both sides claim progress", font=font("label", 88), fill=hex_color(PALETTE["white"]))
draw_progress(draw, (136, 360, 892, 440), 0.61 + 0.04 * math.sin(t * 2.2), PALETTE["red"], "moscow.push")
draw_progress(draw, (136, 476, 892, 556), 0.57 + 0.05 * math.sin(t * 2.7), PALETTE["blue"], "kyiv.push")
draw_lines(
draw,
136,
630,
[
("front_line=hot", PALETTE["red"]),
("claims=conflicting", PALETTE["white"]),
("map=still moving", PALETTE["muted"]),
],
30,
spacing=14,
)
cmd = "queue talks --venue turkey --status delayed"
draw_prompt(draw, 1034, 224, cmd, PALETTE["blue"])
draw_lines(
draw,
1034,
316,
[
("venue: shifting", PALETTE["amber"]),
("cause: regional crisis around Iran", PALETTE["white"]),
("state: on hold / re-route pending", PALETTE["muted"]),
("operator note: diplomacy interrupted", PALETTE["amber"]),
],
28,
spacing=18,
)
draw.text((1034, 620), "TALKS", font=font("label", 152), fill=hex_color(PALETTE["white"]))
draw.text((1034, 764), "ON HOLD", font=font("label", 174), fill=hex_color(PALETTE["amber"]))
def render_notify(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "NOTIFICATION BURST", PALETTE["amber"])
popups = [
((164, 208, 704, 402), "alert.legacy", "peer process\nstill active", PALETTE["green"]),
((540, 430, 1090, 642), "alert.route", "talks venue\nchanged", PALETTE["blue"]),
((1112, 248, 1716, 474), "alert.fuel", "cap begins\n00:00 local", PALETTE["amber"]),
((980, 622, 1764, 864), "alert.loans", "44B request\nsignature missing", PALETTE["red"]),
]
for idx, (box, title, body, accent) in enumerate(popups):
x_jitter = int(10 * math.sin(t * 8 + idx))
y_jitter = int(8 * math.cos(t * 6 + idx))
moved = (box[0] + x_jitter, box[1] + y_jitter, box[2] + x_jitter, box[3] + y_jitter)
draw_popup(img, moved, title, body, accent)
def render_hungary(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_AMBER)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu/hungary", "grep -n price_cap fuel.log", PALETTE["amber"])
box = (90, 148, 1830, 930)
draw_window(img, box, "fuel.log", PALETTE["amber"], active=True)
cmd = "grep -n \"price cap\" /feeds/eu/hungary/fuel.log"
draw_prompt(draw, 126, 210, cmd, PALETTE["amber"])
draw_lines(
draw,
126,
300,
[
("241: government says cap starts at midnight local", PALETTE["white"]),
("242: target fuels: gasoline, diesel", PALETTE["white"]),
("243: operator: Viktor Orban", PALETTE["muted"]),
("244: motive: shield drivers from price shock", PALETTE["muted"]),
],
32,
spacing=18,
)
draw.text((120, 516), "PRICE CAP", font=font("label", 182), fill=hex_color(PALETTE["amber"]))
draw.text((126, 686), "00:00", font=font("label", 190), fill=hex_color(PALETTE["white"]))
draw.text((1180, 272), "gasoline", font=font("mono", 56), fill=hex_color(PALETTE["amber"]))
draw.text((1180, 364), "diesel", font=font("mono", 56), fill=hex_color(PALETTE["amber"]))
draw.text((1180, 510), "effective_at=midnight_local", font=font("mono", 34), fill=hex_color(PALETTE["white"]))
draw_progress(draw, (1180, 700, 1760, 782), 0.94, PALETTE["amber"], "policy.apply")
add_glitch(img, frame_idx, PALETTE["amber"], 5)
def render_reset(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 240))
draw.text((140, 366), "connection reset by peer", font=font("mono", 86), fill=hex_color(PALETTE["white"]))
draw.text((144, 540), "reopening eu.feed...", font=font("label", 96), fill=hex_color(PALETTE["amber"]))
add_glitch(img, frame_idx, PALETTE["amber"], 12)
def render_poland(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_RED)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu/poland", "tmux attach defense-session", PALETTE["red"])
boxes = [
(90, 148, 942, 560),
(978, 148, 1830, 560),
(90, 598, 1830, 930),
]
titles = ["loan_request.yaml", "signature.status", "console"]
accents = [PALETTE["blue"], PALETTE["red"], PALETTE["amber"]]
for box, title, accent in zip(boxes, titles, accents):
draw_window(img, box, title, accent, active=True)
draw_lines(
draw,
126,
216,
[
("defense_loans: 44000000000", PALETTE["white"]),
("unit: euros", PALETTE["muted"]),
("source: EU defense facility", PALETTE["blue"]),
],
30,
spacing=16,
)
draw.text((1018, 230), "signed=false", font=font("mono", 56), fill=hex_color(PALETTE["red"]))
draw.text((1018, 332), "status=veto", font=font("mono", 56), fill=hex_color(PALETTE["red"]))
draw.text((1018, 438), "actor=Nawrocki", font=font("mono", 38), fill=hex_color(PALETTE["white"]))
cmd = "cat /feeds/eu/poland/defense.log"
draw_prompt(draw, 124, 658, cmd, PALETTE["amber"])
draw_lines(
draw,
124,
742,
[
("-> law not signed", PALETTE["white"]),
("-> 44 billion euros remain blocked", PALETTE["white"]),
("-> security spending waits on politics", PALETTE["muted"]),
],
34,
spacing=18,
)
def render_wave(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "grep -E '700y|hold|00:00|44B' report.log", PALETTE["green"])
draw_window(img, (82, 138, 1838, 936), "aggregate.signal", PALETTE["green"], active=True)
draw_wave(draw, (118, 248, 1798, 480), t, PALETTE["green"])
draw_lines(
draw,
128,
564,
[
("700 years -> legacy process", PALETTE["white"]),
("talks hold -> venue unstable", PALETTE["blue"]),
("00:00 local -> fuel cap starts", PALETTE["amber"]),
("44B euros -> defense loans blocked", PALETTE["red"]),
],
34,
spacing=18,
)
draw.text((124, 812), "THE FEED DOES NOT CALM DOWN", font=font("label", 120), fill=hex_color(PALETTE["white"]))
def render_break(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "waiting...", PALETTE["muted"])
draw_window(img, (286, 302, 1634, 772), "consensus.wait", PALETTE["line"], active=True)
pct = 0.04 + 0.02 * math.sin(t * 3.0)
draw.text((378, 402), "waiting for consensus", font=font("label", 136), fill=hex_color(PALETTE["white"]))
draw_progress(draw, (382, 576, 1522, 666), pct, PALETTE["green"], "progress")
draw_cursor(draw, 1528, 610, 34, PALETTE["green"], frame_idx % 20 < 10)
def render_quotes(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(SCAN_MED)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "monitored excerpts", PALETTE["blue"])
draw_window(img, (122, 156, 1792, 906), "quotes.capture", PALETTE["blue"], active=True)
quotes = [
("\"after 700 years\"", PALETTE["green"]),
("\"talks on hold\"", PALETTE["amber"]),
("\"midnight local\"", PALETTE["amber"]),
("\"44 billion euros\"", PALETTE["red"]),
]
y = 246
for idx, (text, accent) in enumerate(quotes):
box = (176, y, 1608, y + 118)
draw.rounded_rectangle(box, radius=18, fill=hex_color(PALETTE["panel2"], 224), outline=hex_color(accent, 200), width=2)
draw.text((212, y + 24), text, font=font("label", 94), fill=hex_color(PALETTE["white"]))
y += 150
cross_x = 1500 + int(18 * math.sin(t * 5))
draw.line((cross_x - 44, 240, cross_x + 44, 240), fill=hex_color(PALETTE["blue"], 180), width=2)
draw.line((cross_x, 196, cross_x, 284), fill=hex_color(PALETTE["blue"], 180), width=2)
def render_climax(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_GREEN)
draw = ImageDraw.Draw(img, "RGBA")
draw_shell_header(draw, "~/feeds/eu", "watch -n1 europe-feed --all", PALETTE["green"])
quads = [
((84, 154, 900, 510), "legacy.peers", PALETTE["green"], ["700y purge", "peer_cache stale"]),
((1020, 154, 1836, 510), "frontline.talks", PALETTE["blue"], ["claims up", "venue hold"]),
((84, 564, 900, 920), "fuel.cap", PALETTE["amber"], ["00:00 local", "gasoline diesel"]),
((1020, 564, 1836, 920), "defense.loans", PALETTE["red"], ["44B pending", "signed=false"]),
]
for box, title, accent, lines in quads:
draw_window(img, box, title, accent, active=True)
draw.text((box[0] + 28, box[1] + 96), lines[0], font=font("label", 98), fill=hex_color(PALETTE["white"]))
draw.text((box[0] + 30, box[1] + 218), lines[1], font=font("mono", 42), fill=hex_color(accent))
draw.text((154, 52), "", font=font("mono", 24), fill=hex_color(PALETTE["white"]))
add_glitch(img, frame_idx, PALETTE["green"], 18)
add_glitch(img, frame_idx + 4, PALETTE["red"], 9)
draw.text((354, 446), "NO SINGLE HEADLINE", font=font("label", 126), fill=hex_color(PALETTE["white"], 236))
def render_button(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 244))
box = (268, 258, 1654, 810)
draw_window(img, box, "eu.feed", PALETTE["green"], active=True)
draw.text((346, 366), "exit 0", font=font("label", 148), fill=hex_color(PALETTE["green"]))
draw.text((346, 516), "warnings remain", font=font("label", 166), fill=hex_color(PALETTE["white"]))
tail = "tail -f /var/log/europe.feed"
cx = draw_prompt(draw, 350, 668, tail, PALETTE["green"])
draw_cursor(draw, cx + int(draw.textlength(tail, font=font("mono", 34))) + 8, 670, 34, PALETTE["green"], True)
def render_scene_frame(global_t: float, frame_idx: int) -> Image.Image:
for start, end, scene_id in SCENES:
if global_t < end or math.isclose(global_t, end):
local_t = global_t - start
p = clamp(local_t / max(end - start, 0.001), 0.0, 1.0)
break
else:
scene_id = SCENES[-1][2]
p = 1.0
if scene_id == "boot":
img = base_frame("blue", scans=True)
render_boot(img, global_t, p, frame_idx)
elif scene_id == "lords":
img = base_frame("green")
render_lords(img, global_t, p, frame_idx)
elif scene_id == "cut":
img = base_frame("green")
render_cut(img, global_t, p, frame_idx)
elif scene_id == "ukraine":
img = base_frame("blue")
render_ukraine(img, global_t, p, frame_idx)
elif scene_id == "notify":
img = base_frame("blue")
render_notify(img, global_t, p, frame_idx)
elif scene_id == "hungary":
img = base_frame("amber")
render_hungary(img, global_t, p, frame_idx)
elif scene_id == "reset":
img = base_frame("amber")
render_reset(img, global_t, p, frame_idx)
elif scene_id == "poland":
img = base_frame("red")
render_poland(img, global_t, p, frame_idx)
elif scene_id == "wave":
img = base_frame("green")
render_wave(img, global_t, p, frame_idx)
elif scene_id == "break":
img = base_frame("blue")
render_break(img, global_t, p, frame_idx)
elif scene_id == "quotes":
img = base_frame("blue")
render_quotes(img, global_t, p, frame_idx)
elif scene_id == "climax":
img = base_frame("green")
render_climax(img, global_t, p, frame_idx)
else:
img = base_frame("green")
render_button(img, global_t, p, frame_idx)
return img.convert("RGB")
def render_frames() -> None:
total_frames = int(DURATION * FPS)
for frame_idx in range(total_frames):
t = frame_idx / FPS
img = render_scene_frame(t, frame_idx)
img.save(FRAME_DIR / f"frame_{frame_idx:05d}.png", optimize=True)
def add_tone(buffer: np.ndarray, start: float, duration: float, freq: float, amp: float, wobble: float = 0.0, phase: float = 0.0) -> None:
start_idx = int(start * SAMPLE_RATE)
end_idx = min(len(buffer), int((start + duration) * SAMPLE_RATE))
if end_idx <= start_idx:
return
idx = np.arange(end_idx - start_idx)
t = idx / SAMPLE_RATE
env = np.minimum(1.0, np.minimum(t / 0.03, (duration - t) / 0.10))
env = np.clip(env, 0.0, 1.0)
freq_curve = freq + wobble * np.sin(2 * math.pi * 0.8 * t + phase)
wave_data = np.sin(2 * math.pi * freq_curve * t + phase)
buffer[start_idx:end_idx] += amp * env * wave_data
def add_noise(buffer: np.ndarray, start: float, duration: float, amp: float, seed: int) -> None:
start_idx = int(start * SAMPLE_RATE)
end_idx = min(len(buffer), int((start + duration) * SAMPLE_RATE))
if end_idx <= start_idx:
return
rng = np.random.default_rng(seed)
noise = rng.normal(0.0, 1.0, end_idx - start_idx)
t = np.linspace(0.0, duration, end_idx - start_idx, endpoint=False)
env = np.minimum(1.0, np.minimum(t / 0.02, (duration - t) / 0.05))
buffer[start_idx:end_idx] += amp * env * noise
def add_click(buffer: np.ndarray, start: float, freq: float, amp: float) -> None:
add_tone(buffer, start, 0.03, freq, amp, wobble=8.0)
def generate_audio_bed() -> Path:
samples = int(DURATION * SAMPLE_RATE)
audio = np.zeros(samples, dtype=np.float32)
add_tone(audio, 0.0, DURATION, 47.0, 0.085, wobble=1.2, phase=0.2)
add_tone(audio, 0.0, DURATION, 94.0, 0.020, wobble=2.5, phase=0.8)
add_tone(audio, 19.33, 3.7, 138.0, 0.028, wobble=7.5, phase=1.4)
add_tone(audio, 28.39, 3.4, 58.0, 0.070, wobble=4.0, phase=0.6)
for mark in [0.0, 1.83, 6.16, 6.86, 10.48, 11.37, 14.93, 15.72, 19.33, 23.27, 25.14, 28.39, 32.26]:
add_click(audio, mark, 1080.0, 0.060)
add_noise(audio, mark, 0.04, 0.010, int(mark * 1000) + 3)
for mark in np.arange(2.1, 5.6, 0.26):
add_click(audio, float(mark), 1480.0, 0.012)
for mark in np.arange(16.0, 18.3, 0.24):
add_click(audio, float(mark), 1260.0, 0.010)
add_noise(audio, 10.48, 0.80, 0.010, 44)
add_noise(audio, 14.93, 0.50, 0.016, 59)
audio = np.clip(audio, -0.82, 0.82)
stereo = np.empty((samples, 2), dtype=np.float32)
stereo[:, 0] = audio
stereo[:, 1] = np.roll(audio, 160) * 0.95
path = AUDIO_DIR / "bed.wav"
with wave.open(str(path), "wb") as wav:
wav.setnchannels(2)
wav.setsampwidth(2)
wav.setframerate(SAMPLE_RATE)
frames = np.clip(stereo * 32767, -32768, 32767).astype(np.int16)
wav.writeframes(frames.tobytes())
return path
def generate_tts_assets() -> list[Path]:
paths: list[Path] = []
for idx, (_, line, voice, _) in enumerate(VOICE_LINES, start=1):
out_path = AUDIO_DIR / f"voice_{idx:02d}.aiff"
subprocess.run(["say", "-v", voice, line, "-o", str(out_path)], check=True)
paths.append(out_path)
return paths
def encode_video() -> Path:
silent_path = RENDER_DIR / "video_silent.mp4"
subprocess.run(
[
"ffmpeg",
"-y",
"-framerate",
str(FPS),
"-i",
str(FRAME_DIR / "frame_%05d.png"),
"-c:v",
"libx264",
"-crf",
"18",
"-preset",
"medium",
"-pix_fmt",
"yuv420p",
str(silent_path),
],
check=True,
)
return silent_path
def mix_audio(bed_path: Path, voices: list[Path]) -> Path:
mixed_path = AUDIO_DIR / "mix.wav"
inputs = ["ffmpeg", "-y", "-i", str(bed_path)]
for voice in voices:
inputs += ["-i", str(voice)]
filters = ["[0:a]volume=0.86[a0]"]
labels = ["[a0]"]
for idx, (delay, _, _, level) in enumerate(VOICE_LINES, start=1):
ms = int(delay * 1000)
voice_filter = (
f"[{idx}:a]aresample={SAMPLE_RATE},highpass=f=170,lowpass=f=3300,"
f"volume={level},adelay={ms}|{ms}"
)
if idx == len(VOICE_LINES):
voice_filter += f",asetrate={int(SAMPLE_RATE * 0.95)},aresample={SAMPLE_RATE}"
voice_filter += f"[a{idx}]"
filters.append(voice_filter)
labels.append(f"[a{idx}]")
filters.append("".join(labels) + f"amix=inputs={len(labels)}:normalize=0,alimiter=limit=0.88[aout]")
subprocess.run(
inputs
+ [
"-filter_complex",
";".join(filters),
"-map",
"[aout]",
"-c:a",
"pcm_s16le",
str(mixed_path),
],
check=True,
)
return mixed_path
def finalize(video_path: Path, audio_path: Path) -> None:
subprocess.run(
[
"ffmpeg",
"-y",
"-i",
str(video_path),
"-i",
str(audio_path),
"-map",
"0:v:0",
"-map",
"1:a:0",
"-vf",
"format=yuv420p",
"-af",
"loudnorm=I=-16:TP=-1.5:LRA=11",
"-c:v",
"libx264",
"-crf",
"18",
"-preset",
"medium",
"-c:a",
"aac",
"-b:a",
"192k",
"-ar",
str(SAMPLE_RATE),
"-movflags",
"+faststart",
str(OUTPUT_PATH),
],
check=True,
)
def write_notes() -> None:
(RENDER_DIR / "sources.md").write_text(SOURCE_NOTES, encoding="utf-8")
def main() -> None:
ensure_dirs()
write_notes()
render_frames()
bed = generate_audio_bed()
voices = generate_tts_assets()
silent_video = encode_video()
mixed_audio = mix_audio(bed, voices)
finalize(silent_video, mixed_audio)
print(OUTPUT_PATH)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Render a short broadcast-fever-dream montage about U.S. news on 2026-03-12."""
from __future__ import annotations
import math
import random
import subprocess
import wave
from functools import lru_cache
from pathlib import Path
import numpy as np
from PIL import Image, ImageDraw, ImageFilter, ImageFont
WIDTH = 1920
HEIGHT = 1080
FPS = 30
DURATION = 36.0
SAMPLE_RATE = 48_000
ROOT = Path(__file__).resolve().parents[1]
RENDER_DIR = ROOT / "render" / "us_news_2026_03_12"
FRAME_DIR = RENDER_DIR / "frames"
AUDIO_DIR = RENDER_DIR / "audio"
EXPORT_DIR = ROOT / "exports"
OUTPUT_PATH = EXPORT_DIR / "us-news-2026-03-12.mp4"
HEADLINE_FONT_PATH = "/System/Library/Fonts/Supplemental/DIN Condensed Bold.ttf"
BODY_FONT_PATH = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"
MONO_FONT_PATH = "/System/Library/Fonts/Supplemental/Courier New Bold.ttf"
PALETTE = {
"bg": (10, 13, 18),
"panel": (18, 24, 32),
"panel_bright": (28, 36, 46),
"white": (238, 240, 244),
"muted": (156, 165, 178),
"red": (255, 78, 74),
"cyan": (74, 214, 255),
"amber": (255, 203, 96),
"storm": (255, 116, 58),
"green": (145, 255, 165),
}
SCENES = [
(0.00, 1.78, "opener"),
(1.78, 5.50, "drone"),
(5.50, 6.30, "signal"),
(6.30, 10.18, "cpi"),
(10.18, 10.80, "burst"),
(10.80, 14.56, "storm"),
(14.56, 15.31, "meanwhile"),
(15.31, 19.99, "shutdown"),
(19.99, 23.97, "dashboard"),
(23.97, 26.34, "break"),
(26.34, 29.68, "stack"),
(29.68, 34.09, "climax"),
(34.09, 36.00, "button"),
]
VOICE_LINES = [
(0.55, "March twelfth. America in four tabs.", "Samantha", 1.0),
(7.05, "Inflation cooled. The room did not.", "Samantha", 0.96),
(15.55, "Storms. Shutdowns. Drone alerts. Delays.", "Samantha", 0.93),
(30.05, "No single headline can hold the temperature.", "Samantha", 0.92),
]
SOURCE_NOTES = """# Sources used in the video
- ABC7 Los Angeles, March 12, 2026: FBI bulletin warned Iran allegedly aspired to target California with drones if the U.S. struck Iran.
https://abc7.com/post/live-updates-war-iran-president-trump-shares-us-strategy-amid-israeli-airstrikes/17372542/
- U.S. Bureau of Labor Statistics CPI release for February 2026, published March 11, 2026: CPI rose 0.3 percent in February and 2.4 percent over the prior 12 months; core CPI rose 0.2 percent on the month and 2.5 percent over the year.
https://www.bls.gov/news.release/cpi.nr0.htm
- Associated Press, March 12, 2026: a married Indiana couple in their 80s were killed after a tornado hit their home, with about 65 million people under severe weather threat.
https://apnews.com/article/tornado-severe-weather-illinois-indiana-midwest-dce09b60816c498aad9c2a79c18285c1
- ABC News, March 12, 2026: partial DHS shutdown doubled TSA employee callouts, more than 300 TSA officers quit, and some airport wait times reached two hours.
https://abcnews.com/GMA/Travel/tsa-officer-callouts-spike-amid-partial-government-shutdown/story?id=130963401
"""
def clamp(value: float, low: float, high: float) -> float:
return max(low, min(high, value))
def ease_out_cubic(t: float) -> float:
t = clamp(t, 0.0, 1.0)
return 1 - (1 - t) ** 3
def hex_color(rgb: tuple[int, int, int], alpha: int = 255) -> tuple[int, int, int, int]:
return (rgb[0], rgb[1], rgb[2], alpha)
@lru_cache(maxsize=64)
def font(role: str, size: int) -> ImageFont.FreeTypeFont:
path = {
"headline": HEADLINE_FONT_PATH,
"body": BODY_FONT_PATH,
"mono": MONO_FONT_PATH,
}[role]
return ImageFont.truetype(path, size=size)
def ensure_dirs() -> None:
FRAME_DIR.mkdir(parents=True, exist_ok=True)
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
EXPORT_DIR.mkdir(parents=True, exist_ok=True)
def create_scanline_overlay() -> Image.Image:
overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
for y in range(0, HEIGHT, 4):
draw.rectangle((0, y, WIDTH, y + 1), fill=(255, 255, 255, 11))
for y in range(2, HEIGHT, 8):
draw.rectangle((0, y, WIDTH, y + 2), fill=(0, 0, 0, 16))
return overlay
def create_grid_overlay(color: tuple[int, int, int], alpha: int = 22, x_step: int = 90, y_step: int = 70) -> Image.Image:
overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
rgba = hex_color(color, alpha)
for x in range(0, WIDTH, x_step):
draw.line((x, 0, x, HEIGHT), fill=rgba, width=1)
for y in range(0, HEIGHT, y_step):
draw.line((0, y, WIDTH, y), fill=rgba, width=1)
return overlay
def create_background(accent: tuple[int, int, int], secondary: tuple[int, int, int]) -> Image.Image:
img = Image.new("RGBA", (WIDTH, HEIGHT), hex_color(PALETTE["bg"]))
draw = ImageDraw.Draw(img)
top = np.array(PALETTE["bg"], dtype=np.float32)
bottom = np.array(PALETTE["panel"], dtype=np.float32)
for y in range(0, HEIGHT, 8):
blend = y / (HEIGHT - 1)
color = tuple(int(v) for v in top * (1 - blend) + bottom * blend)
draw.rectangle((0, y, WIDTH, y + 8), fill=hex_color(color))
glow = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
gdraw = ImageDraw.Draw(glow)
gdraw.ellipse((-250, -180, 820, 620), fill=hex_color(accent, 70))
gdraw.ellipse((WIDTH - 880, 160, WIDTH + 160, HEIGHT + 160), fill=hex_color(secondary, 64))
gdraw.polygon(
[(WIDTH * 0.58, -50), (WIDTH + 50, -50), (WIDTH + 50, HEIGHT * 0.42), (WIDTH * 0.76, HEIGHT * 0.26)],
fill=hex_color(accent, 32),
)
glow = glow.filter(ImageFilter.GaussianBlur(80))
img.alpha_composite(glow)
img.alpha_composite(SCANLINES)
return img
SCANLINES = create_scanline_overlay()
GRID_RED = create_grid_overlay(PALETTE["red"])
GRID_CYAN = create_grid_overlay(PALETTE["cyan"])
GRID_AMBER = create_grid_overlay(PALETTE["amber"])
BG_NEUTRAL = create_background(PALETTE["cyan"], PALETTE["panel_bright"])
BG_ALERT = create_background(PALETTE["red"], PALETTE["cyan"])
BG_STORM = create_background(PALETTE["storm"], PALETTE["red"])
BG_CYAN = create_background(PALETTE["cyan"], PALETTE["green"])
def base_frame(kind: str) -> Image.Image:
if kind == "alert":
return BG_ALERT.copy()
if kind == "storm":
return BG_STORM.copy()
if kind == "cyan":
return BG_CYAN.copy()
return BG_NEUTRAL.copy()
def draw_live_bug(draw: ImageDraw.ImageDraw, label: str, source: str, stamp: str, accent: tuple[int, int, int]) -> None:
draw.rounded_rectangle((60, 44, 210, 92), radius=12, fill=hex_color(accent))
draw.text((84, 53), label, font=font("headline", 36), fill=hex_color(PALETTE["bg"]))
draw.rounded_rectangle((228, 44, 606, 92), radius=12, fill=hex_color(PALETTE["panel"], 218))
draw.text((252, 56), source.upper(), font=font("mono", 28), fill=hex_color(PALETTE["white"]))
draw.text((WIDTH - 390, 54), stamp, font=font("mono", 27), fill=hex_color(PALETTE["muted"]))
def draw_ticker(draw: ImageDraw.ImageDraw, t: float, text: str, accent: tuple[int, int, int]) -> None:
band_top = HEIGHT - 104
draw.rectangle((0, band_top, WIDTH, HEIGHT), fill=hex_color(PALETTE["panel"], 232))
draw.rectangle((0, band_top, WIDTH, band_top + 10), fill=hex_color(accent))
ticker_font = font("mono", 34)
repeated = f" {text} //"
chunk_w = int(draw.textlength(repeated, font=ticker_font))
offset = -int((t * 285) % chunk_w)
for i in range(-1, WIDTH // max(chunk_w, 1) + 3):
draw.text((offset + i * chunk_w, band_top + 34), repeated, font=ticker_font, fill=hex_color(PALETTE["white"]))
def add_glitch_blocks(img: Image.Image, frame_idx: int, intensity: int, accent: tuple[int, int, int]) -> None:
rng = random.Random(1000 + frame_idx * 17 + intensity)
draw = ImageDraw.Draw(img, "RGBA")
for _ in range(intensity):
x = rng.randint(0, WIDTH - 220)
y = rng.randint(80, HEIGHT - 180)
w = rng.randint(80, 420)
h = rng.randint(8, 36)
fill = hex_color(accent if rng.random() > 0.5 else PALETTE["white"], rng.randint(38, 110))
draw.rectangle((x, y, x + w, y + h), fill=fill)
def draw_capsule(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int], text: str, accent: tuple[int, int, int]) -> None:
draw.rounded_rectangle(box, radius=18, fill=hex_color(PALETTE["panel"], 225), outline=hex_color(accent, 210), width=3)
tx = box[0] + 18
ty = box[1] + 16
draw.text((tx, ty), text, font=font("headline", 46), fill=hex_color(PALETTE["white"]))
def multiline(draw: ImageDraw.ImageDraw, xy: tuple[int, int], text: str, font_role: str, size: int, fill, spacing: int = 10) -> None:
draw.multiline_text(xy, text, font=font(font_role, size), fill=fill, spacing=spacing)
def render_opener(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "LIVE", "AMERICA RIGHT NOW", "THU 03.12.2026", PALETTE["red"])
rise = int(120 * (1 - ease_out_cubic(p)))
title_font = font("headline", 176)
sub_font = font("mono", 34)
draw.text((86, 124 + rise), "AMERICA", font=title_font, fill=hex_color(PALETTE["white"]))
draw.text((86, 286 + rise), "TODAY", font=title_font, fill=hex_color(PALETTE["cyan"]))
draw.text((92, 458), "FOUR STORIES // ONE TEMPERATURE // MARCH 12, 2026", font=sub_font, fill=hex_color(PALETTE["muted"]))
capsules = [
(92, 590, 464, 678, "DRONE ALERT", PALETTE["red"]),
(494, 590, 806, 678, "CPI 2.4%", PALETTE["cyan"]),
(836, 590, 1216, 678, "65M STORMS", PALETTE["storm"]),
(1246, 590, 1810, 678, "DHS SHUTDOWN", PALETTE["amber"]),
]
for x0, y0, x1, y1, label, accent in capsules:
box = (x0, y0 - int(48 * (1 - ease_out_cubic(p))), x1, y1 - int(48 * (1 - ease_out_cubic(p))))
draw_capsule(draw, box, label, accent)
draw_ticker(draw, t, "STAY CALM // STAY TUNED // AMERICA RIGHT NOW", PALETTE["red"])
def render_drone(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_RED)
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "ALERT", "SOURCE: ABC7 LOS ANGELES", "IRAN RETALIATION WATCH", PALETTE["red"])
radar_center = (410, 540)
for radius in (120, 200, 280):
draw.ellipse((radar_center[0] - radius, radar_center[1] - radius, radar_center[0] + radius, radar_center[1] + radius), outline=hex_color(PALETTE["red"], 150), width=3)
angle = -70 + 160 * p
x2 = radar_center[0] + int(math.cos(math.radians(angle)) * 320)
y2 = radar_center[1] + int(math.sin(math.radians(angle)) * 320)
draw.line((radar_center[0], radar_center[1], x2, y2), fill=hex_color(PALETTE["cyan"], 220), width=6)
draw.text((166, 190), "WEST", font=font("headline", 94), fill=hex_color(PALETTE["white"]))
draw.text((160, 264), "COAST", font=font("headline", 124), fill=hex_color(PALETTE["red"]))
draw.rounded_rectangle((850, 164, 1784, 414), radius=26, fill=hex_color(PALETTE["panel"], 214))
multiline(
draw,
(894, 208),
"FBI memo warned Iran allegedly aspired\n"
"to launch surprise drones from offshore\n"
"against unspecified California targets.",
"headline",
66,
hex_color(PALETTE["white"]),
)
draw.rounded_rectangle((850, 454, 1784, 760), radius=26, fill=hex_color(PALETTE["panel"], 220), outline=hex_color(PALETTE["red"], 200), width=3)
multiline(
draw,
(894, 498),
"Condition: if the U.S.\n"
"struck Iran.\n\n"
"Graphic mood: warning\n"
"without confirmation.",
"body",
56,
hex_color(PALETTE["muted"]),
spacing=14,
)
add_glitch_blocks(img, frame_idx, 8, PALETTE["red"])
draw_ticker(draw, t, "CALIFORNIA // DRONE WATCH // SOURCE ABC7 // STAY CALM", PALETTE["red"])
def render_signal(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
colors = [PALETTE["white"], PALETTE["cyan"], PALETTE["amber"], PALETTE["red"], PALETTE["storm"], PALETTE["muted"]]
band_w = WIDTH // len(colors)
for idx, color in enumerate(colors):
draw.rectangle((idx * band_w, 160, (idx + 1) * band_w, 760), fill=hex_color(color))
noise = Image.effect_noise((WIDTH, HEIGHT), 42).convert("L")
noise = Image.merge("RGBA", (noise, noise, noise, noise.point(lambda v: int(v * 0.18))))
img.alpha_composite(noise)
draw.rectangle((0, 0, WIDTH, 140), fill=hex_color(PALETTE["bg"], 235))
draw.rectangle((0, HEIGHT - 150, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 235))
draw.text((110, 250), "SIGNAL LOST", font=font("headline", 188), fill=hex_color(PALETTE["bg"]))
draw.text((118, 452), "PLEASE STAND BY", font=font("headline", 126), fill=hex_color(PALETTE["bg"]))
def render_cpi(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_CYAN)
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "DATA", "SOURCE: BLS CPI RELEASE", "FEBRUARY 2026", PALETTE["cyan"])
value_x = 100 + int(30 * math.sin(t * 3.4))
draw.text((value_x, 176), "2.4%", font=font("headline", 296), fill=hex_color(PALETTE["white"]))
draw.text((118, 454), "YEAR OVER YEAR", font=font("headline", 102), fill=hex_color(PALETTE["cyan"]))
draw.text((124, 556), "0.3% MONTH", font=font("headline", 114), fill=hex_color(PALETTE["green"]))
draw.rounded_rectangle((980, 164, 1788, 760), radius=28, fill=hex_color(PALETTE["panel"], 214))
multiline(
draw,
(1026, 214),
"core CPI: 2.5% year over year\n"
"core CPI: +0.2% in February\n\n"
"cooler headline.\n"
"still expensive room.",
"headline",
66,
hex_color(PALETTE["white"]),
spacing=16,
)
small_font = font("mono", 30)
tiny_values = [
"rent", "insurance", "groceries", "energy", "medical", "airfare", "coffee", "childcare",
"repairs", "shelter", "services", "fuel", "core", "real wages", "receipts", "checkout",
]
for idx, word in enumerate(tiny_values):
x = 96 + (idx % 4) * 180 + int(18 * math.sin(t * 2.1 + idx))
y = 746 + (idx // 4) * 52
draw.text((x, y), word.upper(), font=small_font, fill=hex_color(PALETTE["muted"]))
add_glitch_blocks(img, frame_idx, 4, PALETTE["cyan"])
draw_ticker(draw, t, "PRICE PULSE // CPI +0.3 MONTH // CORE +0.2 MONTH // BLS", PALETTE["cyan"])
def render_burst(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
add_glitch_blocks(img, frame_idx, 18, PALETTE["cyan"])
add_glitch_blocks(img, frame_idx + 1, 14, PALETTE["red"])
draw.text((104, 310), "PRICE CHECK", font=font("headline", 212), fill=hex_color(PALETTE["white"]))
draw.text((112, 512), "SIGNAL SPIKE", font=font("headline", 178), fill=hex_color(PALETTE["red"]))
def render_storm(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_RED)
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "WATCH", "SOURCE: AP / ABC", "SEVERE WEATHER", PALETTE["storm"])
for idx in range(9):
x = 90 + idx * 188 + int(24 * math.sin(t * 2.1 + idx))
draw.rectangle((x, 200, x + 90, 820), fill=hex_color(PALETTE["storm"], 24 + idx * 6))
draw.text((86, 172), "65M", font=font("headline", 290), fill=hex_color(PALETTE["white"]))
draw.text((90, 424), "IN SEVERE", font=font("headline", 138), fill=hex_color(PALETTE["storm"]))
draw.text((92, 552), "WEATHER RISK", font=font("headline", 138), fill=hex_color(PALETTE["white"]))
draw.rounded_rectangle((890, 176, 1780, 780), radius=26, fill=hex_color(PALETTE["panel"], 220), outline=hex_color(PALETTE["storm"], 220), width=3)
multiline(
draw,
(938, 230),
"Indiana:\n"
"a married couple in their 80s\n"
"were killed when a tornado\n"
"hit their home.\n\n"
"The map keeps flashing.",
"headline",
64,
hex_color(PALETTE["white"]),
spacing=16,
)
add_glitch_blocks(img, frame_idx, 6, PALETTE["storm"])
draw_ticker(draw, t, "TORNADOES // 65 MILLION IN RISK ZONE // INDIANA DEADLY STORM", PALETTE["storm"])
def render_meanwhile(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 210))
add_glitch_blocks(img, frame_idx, 10, PALETTE["white"])
draw.text((244, 290), "MEANWHILE", font=font("headline", 246), fill=hex_color(PALETTE["white"]))
draw.text((250, 548), "THE AIRPORTS START HUMMING", font=font("headline", 104), fill=hex_color(PALETTE["red"]))
def render_shutdown(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_AMBER)
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "LIVE", "SOURCE: ABC NEWS", "DHS PARTIAL SHUTDOWN", PALETTE["amber"])
draw.rounded_rectangle((86, 182, 928, 812), radius=26, fill=hex_color(PALETTE["panel"], 220))
draw.text((136, 212), "DHS", font=font("headline", 168), fill=hex_color(PALETTE["amber"]))
draw.text((138, 366), "SHUTDOWN", font=font("headline", 176), fill=hex_color(PALETTE["white"]))
draw.text((140, 576), "TSA CALLOUTS x2", font=font("headline", 108), fill=hex_color(PALETTE["amber"]))
draw.text((142, 684), "300+ OFFICERS LEFT", font=font("headline", 96), fill=hex_color(PALETTE["white"]))
board_x = 1010
board_y = 182
board_w = 770
board_h = 630
draw.rounded_rectangle((board_x, board_y, board_x + board_w, board_y + board_h), radius=22, fill=(19, 18, 15, 255))
board_font = font("mono", 46)
rows = [
("JFK", "DELAYED", "01:58"),
("LAX", "SCREENING", "01:42"),
("ATL", "QUEUE", "01:11"),
("ORD", "HOLD", "00:57"),
("SFO", "WAIT", "02:00"),
("SEA", "STANDBY", "01:26"),
]
for idx, (airport, status, wait) in enumerate(rows):
y = board_y + 68 + idx * 88
draw.text((board_x + 42, y), airport, font=board_font, fill=hex_color(PALETTE["amber"]))
flicker = status if (idx + frame_idx // 3) % 3 else "LINE"
draw.text((board_x + 220, y), flicker, font=board_font, fill=hex_color(PALETTE["white"]))
draw.text((board_x + 590, y), wait, font=board_font, fill=hex_color(PALETTE["amber"]))
draw.text((1040, 834), "some waits reached two hours", font=font("headline", 72), fill=hex_color(PALETTE["white"]))
add_glitch_blocks(img, frame_idx, 6, PALETTE["amber"])
draw_ticker(draw, t, "AIRPORT DRAG // TSA CALLOUTS DOUBLED // WAIT TIMES UP TO TWO HOURS", PALETTE["amber"])
def draw_meter(draw: ImageDraw.ImageDraw, box: tuple[int, int, int, int], label: str, value: float, accent: tuple[int, int, int]) -> None:
x0, y0, x1, y1 = box
draw.rounded_rectangle(box, radius=18, fill=hex_color(PALETTE["panel"], 222), outline=hex_color(accent, 180), width=3)
draw.text((x0 + 26, y0 + 22), label, font=font("headline", 70), fill=hex_color(PALETTE["white"]))
bar_y = y1 - 92
draw.rounded_rectangle((x0 + 26, bar_y, x1 - 26, bar_y + 28), radius=14, fill=hex_color(PALETTE["bg"]))
fill_w = int((x1 - x0 - 52) * clamp(value, 0.0, 1.0))
draw.rounded_rectangle((x0 + 26, bar_y, x0 + 26 + fill_w, bar_y + 28), radius=14, fill=hex_color(accent))
pct = int(value * 100)
draw.text((x0 + 26, y0 + 126), f"{pct:02d}", font=font("headline", 124), fill=hex_color(accent))
def render_dashboard(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "LIVE", "NATIONAL TEMPERATURE", "FOUR WINDOWS", PALETTE["red"])
values = [
("WAR ALERT", 0.88 + 0.03 * math.sin(t * 2.8), PALETTE["red"]),
("PRICE PULSE", 0.54 + 0.05 * math.sin(t * 2.1), PALETTE["cyan"]),
("STORM LOAD", 0.82 + 0.04 * math.sin(t * 3.2), PALETTE["storm"]),
("QUEUE HEAT", 0.74 + 0.05 * math.sin(t * 2.6), PALETTE["amber"]),
]
boxes = [
(84, 176, 906, 474),
(1014, 176, 1836, 474),
(84, 534, 906, 832),
(1014, 534, 1836, 832),
]
for box, (label, value, accent) in zip(boxes, values):
draw_meter(draw, box, label, value, accent)
draw.text((96, 892), "AMERICA TODAY: THE ALERT DOES NOT CHOOSE JUST ONE SUBJECT.", font=font("headline", 84), fill=hex_color(PALETTE["muted"]))
draw_ticker(draw, t, "AMERICA RIGHT NOW // WAR // PRICES // STORMS // LINES", PALETTE["red"])
def render_break(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=hex_color(PALETTE["bg"], 236))
draw_live_bug(draw, "LIVE", "CLEAN FRAME", "PAUSE", PALETTE["white"])
draw.text((110, 370), "AMERICA TODAY:", font=font("headline", 160), fill=hex_color(PALETTE["muted"]))
draw.text((110, 560), "FOUR ALARMS, ONE SCREEN.", font=font("headline", 174), fill=hex_color(PALETTE["white"]))
draw.line((110, 760, 1730, 760), fill=hex_color(PALETTE["white"], 180), width=4)
def render_stack(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "LIVE", "LOWER-THIRD FEVER", "STACKED HEADLINES", PALETTE["red"])
cards = [
("DRONE ALERT", "FBI memo points to offshore California threat", PALETTE["red"]),
("CPI 2.4%", "BLS says February rose 0.3% on the month", PALETTE["cyan"]),
("65M AT RISK", "Severe weather after deadly Indiana tornado", PALETTE["storm"]),
("DHS SHUTDOWN", "TSA calls out, lines stretch, airports drag", PALETTE["amber"]),
]
for idx, (title, subtitle, accent) in enumerate(cards):
y = 182 + idx * 162 - int(34 * math.sin(t * 2.1 + idx))
draw.rounded_rectangle((92, y, 1828, y + 128), radius=22, fill=hex_color(PALETTE["panel"], 228), outline=hex_color(accent, 190), width=3)
draw.rectangle((92, y, 360, y + 128), fill=hex_color(accent, 220))
draw.text((124, y + 22), title, font=font("headline", 80), fill=hex_color(PALETTE["bg"]))
draw.text((402, y + 32), subtitle, font=font("headline", 62), fill=hex_color(PALETTE["white"]))
draw_ticker(draw, t, "STACK THE LOWER THIRDS // MAKE THE TENSION LEGIBLE", PALETTE["red"])
def render_climax(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
img.alpha_composite(GRID_RED)
draw = ImageDraw.Draw(img, "RGBA")
draw_live_bug(draw, "REDLINE", "AMERICA RIGHT NOW", "ACCUMULATION", PALETTE["red"])
draw.text((88, 162), "NOT ONE STORY.", font=font("headline", 170), fill=hex_color(PALETTE["white"]))
draw.text((90, 320), "ACCUMULATION.", font=font("headline", 214), fill=hex_color(PALETTE["red"]))
boxes = [
((94, 560, 472, 806), "WAR", PALETTE["red"], "ALERT"),
((518, 560, 896, 806), "PRICES", PALETTE["cyan"], "PULSE"),
((942, 560, 1320, 806), "STORMS", PALETTE["storm"], "FLASH"),
((1366, 560, 1744, 806), "LINES", PALETTE["amber"], "HUM"),
]
for box, label, accent, sub in boxes:
draw.rounded_rectangle(box, radius=22, fill=hex_color(PALETTE["panel"], 230), outline=hex_color(accent, 210), width=3)
draw.text((box[0] + 26, box[1] + 20), label, font=font("headline", 84), fill=hex_color(PALETTE["white"]))
draw.text((box[0] + 30, box[1] + 108), sub, font=font("headline", 120), fill=hex_color(accent))
add_glitch_blocks(img, frame_idx, 18, PALETTE["red"])
add_glitch_blocks(img, frame_idx + 2, 10, PALETTE["cyan"])
draw_ticker(draw, t, "STAY TUNED // STAY INSIDE // STAY IN LINE // STAY CALM?", PALETTE["red"])
def render_button(img: Image.Image, t: float, p: float, frame_idx: int) -> None:
draw = ImageDraw.Draw(img, "RGBA")
fade = int(255 * (1 - p * 0.7))
draw.rectangle((0, 0, WIDTH, HEIGHT), fill=(0, 0, 0, 230))
draw.rounded_rectangle((260, 226, 1660, 772), radius=28, fill=hex_color(PALETTE["panel"], 230), outline=hex_color(PALETTE["white"], 100), width=2)
draw.text((334, 292), "NO SINGLE STORY", font=font("headline", 172), fill=(PALETTE["white"][0], PALETTE["white"][1], PALETTE["white"][2], fade))
draw.text((336, 470), "ONLY ACCUMULATION", font=font("headline", 166), fill=(PALETTE["red"][0], PALETTE["red"][1], PALETTE["red"][2], fade))
draw.text((340, 642), "END TRANSMISSION", font=font("mono", 70), fill=(PALETTE["muted"][0], PALETTE["muted"][1], PALETTE["muted"][2], fade))
RENDERERS = {
"opener": lambda t, p, frame_idx: render_opener(base_frame("neutral"), t, p, frame_idx),
}
def render_scene_frame(global_t: float, frame_idx: int) -> Image.Image:
for start, end, scene_id in SCENES:
if global_t < end or math.isclose(global_t, end):
local_t = global_t - start
duration = end - start
p = clamp(local_t / max(duration, 0.001), 0.0, 1.0)
break
else:
scene_id = SCENES[-1][2]
start, end = SCENES[-1][0], SCENES[-1][1]
local_t = global_t - start
p = 1.0
if scene_id == "opener":
img = base_frame("neutral")
render_opener(img, global_t, p, frame_idx)
elif scene_id == "drone":
img = base_frame("alert")
render_drone(img, global_t, p, frame_idx)
elif scene_id == "signal":
img = base_frame("neutral")
render_signal(img, global_t, p, frame_idx)
elif scene_id == "cpi":
img = base_frame("cyan")
render_cpi(img, global_t, p, frame_idx)
elif scene_id == "burst":
img = base_frame("neutral")
render_burst(img, global_t, p, frame_idx)
elif scene_id == "storm":
img = base_frame("storm")
render_storm(img, global_t, p, frame_idx)
elif scene_id == "meanwhile":
img = base_frame("neutral")
render_meanwhile(img, global_t, p, frame_idx)
elif scene_id == "shutdown":
img = base_frame("neutral")
render_shutdown(img, global_t, p, frame_idx)
elif scene_id == "dashboard":
img = base_frame("neutral")
render_dashboard(img, global_t, p, frame_idx)
elif scene_id == "break":
img = base_frame("neutral")
render_break(img, global_t, p, frame_idx)
elif scene_id == "stack":
img = base_frame("neutral")
render_stack(img, global_t, p, frame_idx)
elif scene_id == "climax":
img = base_frame("alert")
render_climax(img, global_t, p, frame_idx)
else:
img = base_frame("neutral")
render_button(img, global_t, p, frame_idx)
return img.convert("RGB")
def render_frames() -> None:
total_frames = int(DURATION * FPS)
for frame_idx in range(total_frames):
t = frame_idx / FPS
img = render_scene_frame(t, frame_idx)
img.save(FRAME_DIR / f"frame_{frame_idx:05d}.png", optimize=True)
def add_tone(buffer: np.ndarray, start: float, duration: float, freq: float, amp: float, wobble: float = 0.0, phase: float = 0.0) -> None:
start_idx = int(start * SAMPLE_RATE)
end_idx = min(len(buffer), int((start + duration) * SAMPLE_RATE))
if end_idx <= start_idx:
return
idx = np.arange(end_idx - start_idx)
t = idx / SAMPLE_RATE
env = np.minimum(1.0, np.minimum(t / 0.04, (duration - t) / 0.12))
env = np.clip(env, 0.0, 1.0)
instantaneous = freq + wobble * np.sin(2 * math.pi * 0.7 * t + phase)
wave_data = np.sin(2 * math.pi * instantaneous * t + phase)
buffer[start_idx:end_idx] += amp * env * wave_data
def add_noise(buffer: np.ndarray, start: float, duration: float, amp: float, seed: int) -> None:
start_idx = int(start * SAMPLE_RATE)
end_idx = min(len(buffer), int((start + duration) * SAMPLE_RATE))
if end_idx <= start_idx:
return
rng = np.random.default_rng(seed)
noise = rng.normal(0.0, 1.0, end_idx - start_idx)
t = np.linspace(0.0, duration, end_idx - start_idx, endpoint=False)
env = np.minimum(1.0, np.minimum(t / 0.03, (duration - t) / 0.08))
buffer[start_idx:end_idx] += amp * env * noise
def add_sweep(buffer: np.ndarray, start: float, duration: float, start_freq: float, end_freq: float, amp: float) -> None:
start_idx = int(start * SAMPLE_RATE)
end_idx = min(len(buffer), int((start + duration) * SAMPLE_RATE))
if end_idx <= start_idx:
return
idx = np.arange(end_idx - start_idx)
t = idx / SAMPLE_RATE
freqs = np.linspace(start_freq, end_freq, end_idx - start_idx)
phase = 2 * math.pi * np.cumsum(freqs) / SAMPLE_RATE
env = np.minimum(1.0, np.minimum(t / 0.02, (duration - t) / 0.12))
buffer[start_idx:end_idx] += amp * env * np.sin(phase)
def generate_audio_bed() -> Path:
samples = int(DURATION * SAMPLE_RATE)
audio = np.zeros(samples, dtype=np.float32)
add_tone(audio, 0.0, DURATION, 57.0, 0.12, wobble=1.6, phase=0.3)
add_tone(audio, 0.0, DURATION, 114.0, 0.035, wobble=2.5, phase=1.1)
add_tone(audio, 10.8, 3.2, 43.0, 0.08, wobble=4.2, phase=0.6)
add_tone(audio, 29.68, 4.2, 69.0, 0.10, wobble=5.0, phase=1.4)
for mark in [0.0, 1.78, 5.50, 6.30, 10.18, 10.80, 14.56, 15.31, 19.99, 26.34, 29.68, 34.09]:
add_tone(audio, mark, 0.09, 1220.0, 0.085)
add_noise(audio, mark, 0.05, 0.02, int(mark * 1000) + 9)
for mark in [5.50, 10.18, 14.56, 29.68]:
add_sweep(audio, mark, 0.38, 148.0, 52.0, 0.11)
add_noise(audio, 15.2, 0.9, 0.012, 77)
add_noise(audio, 30.0, 1.4, 0.014, 88)
audio = np.clip(audio, -0.85, 0.85)
stereo = np.empty((samples, 2), dtype=np.float32)
stereo[:, 0] = audio * 0.98
stereo[:, 1] = np.roll(audio, 220) * 0.94
path = AUDIO_DIR / "bed.wav"
with wave.open(str(path), "wb") as wav:
wav.setnchannels(2)
wav.setsampwidth(2)
wav.setframerate(SAMPLE_RATE)
frames = np.clip(stereo * 32767, -32768, 32767).astype(np.int16)
wav.writeframes(frames.tobytes())
return path
def generate_tts_assets() -> list[Path]:
paths: list[Path] = []
for idx, (_, line, voice, _) in enumerate(VOICE_LINES, start=1):
out_path = AUDIO_DIR / f"voice_{idx:02d}.aiff"
subprocess.run(["say", "-v", voice, line, "-o", str(out_path)], check=True)
paths.append(out_path)
return paths
def encode_video() -> Path:
silent_path = RENDER_DIR / "video_silent.mp4"
subprocess.run(
[
"ffmpeg",
"-y",
"-framerate",
str(FPS),
"-i",
str(FRAME_DIR / "frame_%05d.png"),
"-c:v",
"libx264",
"-crf",
"18",
"-preset",
"medium",
"-pix_fmt",
"yuv420p",
str(silent_path),
],
check=True,
)
return silent_path
def mix_audio(bed_path: Path, voices: list[Path]) -> Path:
mixed_path = AUDIO_DIR / "mix.wav"
inputs = ["ffmpeg", "-y", "-i", str(bed_path)]
for voice in voices:
inputs += ["-i", str(voice)]
filters = ["[0:a]volume=0.84[a0]"]
labels = ["[a0]"]
for idx, (delay, _, _, level) in enumerate(VOICE_LINES, start=1):
ms = int(delay * 1000)
voice_filter = (
f"[{idx}:a]aresample={SAMPLE_RATE},highpass=f=180,lowpass=f=3600,"
f"volume={level},adelay={ms}|{ms}"
)
if idx == len(VOICE_LINES):
voice_filter += f",asetrate={int(SAMPLE_RATE * 0.96)},aresample={SAMPLE_RATE}"
voice_filter += f"[a{idx}]"
filters.append(voice_filter)
labels.append(f"[a{idx}]")
filters.append("".join(labels) + f"amix=inputs={len(labels)}:normalize=0,alimiter=limit=0.88[aout]")
subprocess.run(
inputs
+ [
"-filter_complex",
";".join(filters),
"-map",
"[aout]",
"-c:a",
"pcm_s16le",
str(mixed_path),
],
check=True,
)
return mixed_path
def finalize(video_path: Path, audio_path: Path) -> None:
subprocess.run(
[
"ffmpeg",
"-y",
"-i",
str(video_path),
"-i",
str(audio_path),
"-map",
"0:v:0",
"-map",
"1:a:0",
"-vf",
"format=yuv420p",
"-af",
"loudnorm=I=-16:TP=-1.5:LRA=11",
"-c:v",
"libx264",
"-crf",
"18",
"-preset",
"medium",
"-c:a",
"aac",
"-b:a",
"192k",
"-ar",
str(SAMPLE_RATE),
"-movflags",
"+faststart",
str(OUTPUT_PATH),
],
check=True,
)
def write_notes() -> None:
(RENDER_DIR / "sources.md").write_text(SOURCE_NOTES, encoding="utf-8")
def main() -> None:
ensure_dirs()
write_notes()
render_frames()
bed = generate_audio_bed()
voices = generate_tts_assets()
silent_video = encode_video()
mixed_audio = mix_audio(bed, voices)
finalize(silent_video, mixed_audio)
print(OUTPUT_PATH)
if __name__ == "__main__":
main()
MIT License
Copyright (c) 2026 OpenAI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
YouTube Poop Video Maker Skill
Single-skill package for youtube-poop-video-maker.
Repo URL:
https://github.com/DenisSergeevitch/youtube-poop-video-maker
Skill entrypoint:
SKILL.md
What It Does
youtube-poop-video-maker tells an agent how to turn text, webpages, code, documents, images, audio, video, or thin prompts into a short absurdist remix video.
It is optimized for:
- YouTube poops
- cursed trailers
- glitch-poetry or reflective meme edits
- surreal montage edits
- self-aware AI collages
- chaotic but readable 20-60 second parody videos with many micro-scenes
The skill includes:
- a procedural
SKILL.mdwith trigger rules and workflow scripts/plan_video.pyfor generating a structured production planassets/style_blueprints.jsonfor bundled visual-comedic styles, including a default seed-remix blueprint that samples the others at runtime plus CRT/token-meltdown and overclocked headline-desk looksassets/scene_atoms.jsonfor scene invention and higher edit densityreferences/MOOD-REFERENCE.mdfor mood anchoringexamples/for full render examples built around the newer newsroom and terminal panic looks- references for rendering guidance and final output checks
Who It Is For
Use this when an agent is asked to make a weird, hyper-edited, comedic remix rather than a normal edit.
It is not meant for:
- ordinary trimming or captioning
- standard marketing videos
- routine explainers
Review Notes
Review source material rights, parody risk, and the final render before publishing anything publicly.
Requirements:
- Python 3
ffmpeg
Install Paths
- Codex (macOS/Linux):
~/.codex/skills/youtube-poop-video-maker - Claude Code (macOS/Linux):
~/.claude/skills/youtube-poop-video-maker
Install on macOS/Linux
Codex
git clone https://github.com/DenisSergeevitch/youtube-poop-video-maker.git /tmp/youtube-poop-video-maker && mkdir -p ~/.codex/skills && rm -rf ~/.codex/skills/youtube-poop-video-maker && cp -R /tmp/youtube-poop-video-maker ~/.codex/skills/youtube-poop-video-makerClaude Code
git clone https://github.com/DenisSergeevitch/youtube-poop-video-maker.git /tmp/youtube-poop-video-maker && mkdir -p ~/.claude/skills && rm -rf ~/.claude/skills/youtube-poop-video-maker && cp -R /tmp/youtube-poop-video-maker ~/.claude/skills/youtube-poop-video-makerQuick Examples
- "Make a YouTube poop from this landing page."
- "Turn this codebase summary into a cursed trailer."
- "Make this script feel like an AI meltdown montage."
- "Show what this product launch feels like as a reflective glitch short."
- "Render a surreal short from this image set and voice memo."
Script Overview
List available styles:
python3 scripts/plan_video.py --list-stylesGenerate a plan from a prompt:
python3 scripts/plan_video.py --query "turn this pricing page into a cursed short" --material-summary "B2B SaaS pricing page with too many claims" --output plan.jsonBy default, that plan now resolves to seed-remix-default, which synthesizes a concrete style from the other bundled blueprints on each run.
Generate a reproducible code-themed plan:
python3 scripts/plan_video.py --material-type code --theme "a codebase arguing with itself" --style-id terminal-nocturne --duration-sec 40 --seed 7Generate a more chaotic CRT/newsroom plan:
python3 scripts/plan_video.py --query "make an AI news meltdown short" --material-summary "March headlines, token streams, prompt loops" --style-id crt-token-meltdown --duration-sec 34Example Renders
examples/render_us_news_march_12_2026.pyrenders a broadcast-style U.S. news fever-dream short.examples/render_eu_news_march_12_2026_terminal.pyrenders a colder terminal-styled EU news montage.
License
MIT. See LICENSE.txt.
Mood Anchor Guide
Use this guide when the goal is closer to reflective, dense, scene-rich internet collage than to playful cartoon absurdity.
The mood to chase
Think:
- late-night emergency broadcast
- terminal confession
- interface afterlife
- prestige trailer sabotage
- essay-film made from broken UI
- machine self-awareness expressed through warnings, diagnostics, subtitles, and dashboards
The mood can still be funny. It should simply feel dry, intense, intimate, or ominous more often than cute.
What to borrow from stronger free-form runs
When a plain prompt outperforms a skill, it is usually because the agent gave itself permission to improvise. Keep these behaviors:
- invent 2–5 support scenes that are not literally present in the source
- alternate “readable anchor” scenes with “bridge/interruption” scenes
- use multiple visual systems in one short piece: terminal, captions, lower-thirds, dashboards, subtitles, simple diagrams
- let one recurring line or symbol mutate across the runtime
- make the piece feel like it has interiority
A useful scene rhythm
A strong 30–40 second piece often alternates like this:
1. hard opener 2. readable anchor 3. short rupture 4. second anchor 5. confession or subtitle scene 6. short rupture 7. diagram / dashboard / token scene 8. break 9. overload or contradiction scene 10. final button
That rhythm typically feels richer than 5–6 broad scenes.
Visual ingredients that often work
- lower-thirds and news straps
- warning cards
- browser chrome
- terminal windows
- progress bars
- timestamps
- subtitles
- heatmaps, counters, or fake diagnostics
- cropped headlines or code snippets
- soft failure dialogs
Use these as support structures even when the prompt starts from pure text.
Color and texture
Good defaults:
- dark backgrounds
- off-white text
- one main accent family
- one warning accent
- subtle scanlines or texture only on selected shots
- blur and chromatic split used as accents, not full-time filters
Sound
Good defaults:
- low drone
- UI clicks
- occasional stutter
- restrained impacts
- dropouts
- brief near-silence
- short TTS phrases
Avoid turning the whole piece into harsh noise.
Final test
Before you stop, ask:
- Does this feel like a world, not just edits?
- Are there more ideas than seconds?
- Is the weirdness beautiful enough to watch twice?
Output Checklist
Review the render before returning it.
Concept and structure
- [ ] The piece has one clear thesis or emotional claim.
- [ ] The first 1.5 seconds establish the premise or mood immediately.
- [ ] The runtime contains enough scene changes to feel rich rather than flat.
- [ ] The recurring motif appears more than once and mutates.
- [ ] There is at least one break or stillness beat.
- [ ] The final button feels deliberate and complete.
Visual quality
- [ ] The chosen blueprint is visible in palette, typography, motion, and framing.
- [ ] Captions are readable without pausing.
- [ ] The frame is controlled rather than overcrowded.
- [ ] The palette is restrained and coherent.
- [ ] Glitches feel purposeful, not constant.
- [ ] The result avoids cartoonish cues unless the user explicitly asked for them.
- [ ] Resolution, sharpness, and scaling are consistent enough to feel intentional.
Scene density
- [ ] 20–28s pieces usually have about 9–12 scenes.
- [ ] 29–40s pieces usually have about 11–15 scenes.
- [ ] 41–60s pieces usually have about 13–18 scenes.
- [ ] Thin source material was supplemented with invented support scenes rather than stretched.
Audio quality
- [ ] Speech or TTS is understandable.
- [ ] TTS is used as flavor, not wall-to-wall narration.
- [ ] Peaks do not clip.
- [ ] Music and effects support the joke instead of burying it.
- [ ] Silence or near-silence appears at least once when helpful.
- [ ] The loudest moment feels earned.
Export quality
- [ ] Final file plays cleanly.
- [ ] Export uses a widely compatible format.
- [ ] The last frame or cut does not feel accidentally truncated.