analog-horror-psa

star 78

How to make an analog-horror PSA short — the stenciled-pictogram / robo-broadcast / VHS-overlay format (faceless, 10 scenes × ~3s, 9:16, ~30s total). A domain overlay on the standard pipeline that supplies the IF / DO-NOT / BUT / AND scenario structure, the locked yellow-on-black 1970s-civil-defense pictogram visual STYLE, the chroma-key-to-alpha rule (icons must be transparent PNGs before composition), the robo-PSA voice profile (ElevenLabs "Alerter" community voice + ALL CAPS input + stability ~0.5 + style 0), the layered VHS-noise overlay stack (SnowCanvas + VcrTrackingCanvas + MobiusScanlines + MobiusWobble), the 5-layer RGB chromatic-split for icons + captions, the SMPTE colour-bars climax (3 wide vertical bars + filter blur(2.5px) mandatory), and the `-tune grain` CRF 30 final-encode rule for noise-heavy renders. Works for ANY PSA topic — "your fridge is not your fridge", "your phone is not your phone". USE WHEN the user asks for an analog horror PSA / "creepy PSA-style short" / fake-emergency-broadcast

alecs5am By alecs5am schedule Updated 6/11/2026

name: analog-horror-psa namespace: user description: >- How to make an analog-horror PSA short — the stenciled-pictogram / robo-broadcast / VHS-overlay format (faceless, 10 scenes × ~3s, 9:16, ~30s total). A domain overlay on the standard pipeline that supplies the IF / DO-NOT / BUT / AND scenario structure, the locked yellow-on-black 1970s-civil-defense pictogram visual STYLE, the chroma-key-to-alpha rule (icons must be transparent PNGs before composition), the robo-PSA voice profile (ElevenLabs "Alerter" community voice + ALL CAPS input + stability ~0.5 + style 0), the layered VHS-noise overlay stack (SnowCanvas + VcrTrackingCanvas + MobiusScanlines + MobiusWobble), the 5-layer RGB chromatic-split for icons + captions, the SMPTE colour-bars climax (3 wide vertical bars + filter blur(2.5px) mandatory), and the -tune grain CRF 30 final-encode rule for noise-heavy renders. Works for ANY PSA topic — "your fridge is not your fridge", "your phone is not your phone". USE WHEN the user asks for an analog horror PSA / "creepy PSA-style short" / fake-emergency-broadcast video / VHS-aesthetic warning / stenciled-pictogram horror short for TikTok / Reels / Shorts, with no specific existing video to reproduce. This is a niche SKILL (generalized), not a remix TEMPLATE. For "remix this exact PSA but swap the subject", use the remix path in docs/skills-vs-templates.md.

Trigger

FIRES on a generic analog-horror PSA brief: "make an analog-horror PSA about ", "creepy emergency broadcast short", "VHS-style warning video", "PSA where is actually ", "fake civil-defense alert for ", "your is not your ", "stenciled-pictogram horror short". Any subject — the skill is subject-agnostic.

DO NOT FIRE when:

  • The user points at one specific PSA video to reproduce → that is the remix path. See docs/skills-vs-templates.md.
  • The user wants a different horror register (cosmic-horror, found-footage, jumpscare reaction) → match a different niche skill or run freeform via scenarist.
  • The user wants a still poster of the same vibe → use /poster with an analog-horror aesthetic.

What this skill is

A generalized format overlay, not a finished PSA. It does not name a subject, a topic, or a voice line — it tells the pipeline HOW the stenciled-pictogram analog-horror PSA is built so a strong one comes out for whatever the user is warning about. It runs through the normal pipeline (scenarist → art-director → editor); it does not bypass intake gates or the quality gates.

Hard invariants

  • All generation routes through ralphy generate (image / sfx / voiceover) — no raw API. Read MODELS.md before naming any model id.
  • Lock the visual STYLE on a single prototype icon BEFORE generating the other nine. The 10 pictograms must be siblings of one approved master. Iterating mid-batch costs $1.00+ on prototype gens (analog-horror-fridge-001 burned $1.00 of v3 → v10 iterations). Pass --ref <approved-master> on every sibling gen.
  • Icons must be chroma-keyed to alpha BEFORE composition. Otherwise the near-black icon bg shows as a visible rectangle on the #1a1a1a Remotion / HyperFrames underlay. Two-pass ffmpeg colorkey: colorkey=0x000000:0.20:0.08,colorkey=0xFFFFFF:0.20:0.05 — second pass catches any icon that generates on a white outer panel.
  • Verify ElevenLabs voice exists before bulk regen. Voice IDs (especially community / "Alerter" library voices) can disappear between sessions. xh GET /v1/voices/<id> | jq .voice_id as a pre-flight.
  • Sequential VO + SFX generation. ElevenLabs 429s on ≥3 concurrent. Always serial.
  • ralphy queue daemon trips an OpenRouter burst-cap on image gen. Use a sequential bash for-loop for the 10 sibling icons, not --queue.
  • Loudnorm AFTER all clips are final, never in the same loop as gen. A failed regen mid-loop double-norms the surviving clips.
  • -tune grain CRF 30 on the final encode. Default x264 CRF 18 produces 190 MB / 30s renders because canvas-snow is max-entropy noise. ralphy video optimize --crf 30 --tune grain drops it ~12.8× with zero perceived loss.
  • Append-only on regen. Re-rolling a voiceover saves the previous take as <slot>-pre-<change>.mp3. Never overwrite.

The niche, in one paragraph

An analog-horror PSA lives or dies on broadcast-monotone voice + locked stencil pictogram visuals + the layered analog-tape decay overlay. The format mimics a 1970s-80s civil-defense emergency broadcast: flat yellow stenciled icons on pure black, a robotic announcer reading short directive lines, VHS hiss + scanlines + tracking glitches as the bed, and an SMPTE-colour-bars "signal lost" climax. The horror is in the register (cold, official, repeating) more than in the content of any single line.

The fixed 10-scene structure (do not restructure — only fill the slots)

Each scene is ~3 seconds, 9:16 portrait, total ~30s. The IF / DO-NOT / BUT / AND template gives the format its hypnotic cadence:

Scene Beat role VO shape (ALL CAPS)
1 Title-card hook "COMPLIANCE BULLETIN ." (EBS-style red <digit>-D icon)
2 Authority frame "THIS IS A RESIDENTIAL BEHAVIORAL ALERT."
3 IF — observation "IF YOUR ."
4 IF — escalation "IF YOUR ."
5 DO-NOT — directive "DO NOT LOOK IN ITS EYES." / "DO NOT ."
6 BUT — contradiction "BUT IT WILL ."
7 AND — confirmation "AND YOU WILL ."
8 Specifics "YOUR IS NOT YOUR ." (the format's signature line)
9 Direct address "."
10 Signal-lost climax (no VO — SMPTE colour-bars + layered growl SFX)

The IF / DO-NOT / BUT / AND structure is the format's recognizable cadence; deviating breaks the genre.

The locked pictogram visual STYLE

Every icon is a flat solid yellow #FFD400 shape on a pure black #000000 background, primitive / civil-defense register, no internal texture, no outline. Generate ONE prototype with --ref against the genre reference, get user approval, then batch all 9 siblings with the prototype as --ref.

Prompt scaffold (verbatim — drop in the subject):

Generate a pictogram icon that EXACTLY MATCHES the style of the reference image
(the yellow sitting-dog pictogram on pure black).

REQUIREMENTS:
- PURE BLACK BACKGROUND hex 000000 (perfectly flat solid black, NO texture,
  NO grain, NO scanlines, NO noise on the background).
- FLAT SOLID YELLOW SHAPES hex FFD400 (completely uniform yellow fill, NO
  internal texture, NO grain, NO gradient, NO highlights — just flat yellow).
- Slightly imperfect / hand-traced outer edges (subtle wobble like a stencil),
  NO separate outline color — just flat yellow shapes directly on flat black.
- Style: simplified primitive pictogram, like a 1970s civil-defense PSA icon
  or a road-safety warning sign — bold readable shapes, no fine detail,
  no anatomical realism.
- Composition: subject occupies about 55 percent of frame, perfectly centered,
  large solid-black margin all sides. Vertical 9:16.
- No text, no logos, no watermarks, no noise.

Subject for THIS icon: <SUBJECT SPECIFIC TO SCENE>

Negative: textured background, dark gray background, scanlines on bg, noise on bg, paper-grain, internal texture in shape, grain inside, spray paint inside, dotted fill, halftone fill, gradient fill, highlights inside, shadows inside, dark outline, black border, double outline, pixel art, 8-bit, blocky voxel, smooth perfect vector curves, 3d render, photorealism, depth of field, glow, neon, watermark, logo, signature, fur detail, eyes detail, mouth detail, realistic anatomy.

For scene-01, swap yellow for red #DD2020 to render an EBS-style <digit>-D title-card.

The robo-PSA voice profile

The voice carries the whole horror layer. Defaults that worked on analog-horror-fridge-001:

  • Voice: ElevenLabs "Alerter" (community library, user-saved) is the validated winner. Default-library voices (Ava, Marcus) read too human even on a flat prompt. Verify the voice exists in the user's library via xh GET /v1/voices/<id> BEFORE bulk regen — community voices disappear between sessions.
  • Settings: stability ~0.5, style 0. Strict broadcaster cadence with minimal inflection. The CLI does not yet expose these flags — fall back to the ElevenLabs UI when the keeper take is on the line.
  • Input text: ALL CAPS. Forces flatter ElevenLabs delivery. Not a guarantee (the model still adds some emotion) but shifts the prior toward broadcast-monotone.
  • Per-scene clips: Sequential generation. ElevenLabs 429s on ≥3 concurrent.
  • Loudnorm: ralphy audio loudnorm --target -16 on each clip AFTER all 10 are final. Never in the same loop as gen.

The VHS-noise overlay stack

Composed in HyperFrames (or Remotion). Four cooperating canvas / wrapper layers:

  1. MobiusWobble — parent wrapper, applies tiny per-frame translate(${x}px, ${y}px) jitter (1px alternating every 2-3 frames) to the entire content tree. Cheap, dramatic, no per-component instrumentation.
  2. SnowCanvas — pure white noise canvas (Uint32Array(255*Math.random()) per pixel per frame) at opacity 0.2 on #aaa background underneath the icon layer.
  3. VcrTrackingCanvas — Mobius1-port algorithm: 20 noise points per frame, each spawns a tail of getRandomInt(1, 50) 2×2 white rects walking ±1-4px horizontally. filter: blur(1px). Mounts as <canvas> with useRef + useEffect([frame]) so it redraws every frame.
  4. MobiusScanlines — repeating-linear-gradient scanline overlay on mixBlendMode: "multiply".

The reusable components belong at src/lib/components/overlays/AnalogTV.tsx (move + document them when the matching templates/entertainment-viral/analog-horror-psa/ scaffold gets extracted).

The 5-layer RGB chromatic split (icons + captions)

What sells "analog horror" in 2026, applied to both icon and caption on the same beat:

Layer Shift Hue rotate Blur Opacity
Red ghost -12px X -40° 4 1.0
Green ghost +12px X +60° 4 1.0
Blue ghost (glitch) random ±16px every 0.6s 180° 4 0.15-1.0 flicker (4-frame seed)
Wide soft white-halo 0 0 22 0.55
Sharp core 0 0 0.9 1.0

All on mixBlendMode: "screen" except the sharp core. The blue ghost flickering down to 15% opacity on a 4-frame seed reads as "lost color channel" dropout.

Glitch beat synchronized across icon + caption — both use frame % 18 <= 1 ? 16 : 0 for the +X jump and frame % 18 >= 9 && <= 10 ? -16 : 0 for the −X jump. When the icon jerks, the caption jerks the same way — sells the "this is one analog signal" illusion.

The SMPTE colour-bars climax (scene-10)

Confetti dots look CSS-generated. The reference is real SMPTE bars:

  • 3 wide vertical bars (cyan / green / magenta) top 78% + 5 narrower bars (multi-color) bottom 22%.
  • filter: blur(2.5px)non-negotiable. Without the blur it reads as clean CSS, not a degraded analog channel.
  • Horizontal sync-jitter offset (±9px X, ±4px Y).
  • Rolling tracking band sweeping down.
  • Chromatic-aberration ghost layer with hue-rotate.
  • Scanline multiply overlay.
  • 28 dust specks + 5 white scratch lines.

No VO. Layer 4 climax-growl SFX variants at staggered offsets (1.5s, 1.8s, 1.9s, 2.1s) with volumes 0.55-0.60 — single growl sounds like "chihuahua yipping", layered 4 reads as "monster".

Audio bed levels

Validated on analog-horror-fridge-001:

  • VHS-hiss bed: 0.22 (constant under everything)
  • Low-drone bed: 0.12 (constant)
  • Music bed (synth-tension track, yt-dlp from a YouTube reference): 0.35-0.40 — sits ABOVE the static-noise bed, just under the VO. Initial 0.16 was inaudible; the music has to do its dread-building job, not whisper underneath.
  • Per-scene VO: 1.0
  • Per-scene static-pop: 0.55
  • Per-scene extra SFX: 0.4-0.7

Default model stack (verify against MODELS.md)

  • Style-discovery prototype: openai/gpt-5.4-image-2 with --ref <genre-frame>. Best at "match a reference image exactly" with verbose style prompts. One prototype, one yes/no, iterate ONLY this slot.
  • Style-locked batch (after prototype approved): google/gemini-3-pro-image-preview (nano-banana). With --ref <approved-prototype> + per-icon subject prompt, holds the locked style across all 9 siblings reliably. Cheaper + faster than gpt-5.4 for consistency-across-batch.
  • VO: ElevenLabs eleven_multilingual_v2, voice "Alerter" (or equivalent broadcaster-style community voice). Verify voice exists first.
  • SFX: ElevenLabs sound_generation_v2 via ralphy generate sfx. 4 variants per climax growl, layered.
  • Music: ralphy ref pull <yt-music-url> --audio-only for a synth-tension bed. Generated music (ElevenLabs Music) wasn't tried on the reference project; yt-dlp pull from a user-supplied YouTube reference is the validated 2-minute path.
  • Render: HyperFrames composition → ralphy render <id>ralphy video optimize --crf 30 --tune grain --preset veryslow (mandatory for noise-heavy comps).

Failure modes

  • Iterating prototypes without --ref → 4-5 wrong styles burned ($0.80-$1.00). Prevention: pass a genre reference frame as --ref from the FIRST prototype.
  • Forgetting the chroma-key step → visible rectangle around every icon on the dark Remotion bg. Prevention: two-pass colorkey=0x000000:0.20:0.08,colorkey=0xFFFFFF:0.20:0.05 to artifacts/images-keyed/ before composition.
  • Single-growl climax → reads as "chihuahua yipping". Prevention: 4 variants layered at staggered offsets.
  • Confetti-dot climax → CSS-generated, not analog. Prevention: SMPTE bars + filter: blur(2.5px) (non-negotiable).
  • Loudnorm inside the gen loop → double-norms if any regen fails. Prevention: gen all → verify durations → loudnorm pass as a separate step.
  • Default Remotion CRF 18 on noise-heavy comp → 190 MB / 30s output (unsharable to social). Prevention: ralphy video optimize --crf 30 --tune grain post-render.
  • Picking too-emotional default-library voice and iterating regens → wasted ~30 calls. Prevention: switch voice (community "Alerter") instead of fighting; fall back to ElevenLabs UI when CLI doesn't expose stability / style.
  • ralphy queue for image batches → 9/10 jobs hit 403 Key limit exceeded. Prevention: sequential bash for-loop on the 10 sibling icons.

Workflow

  1. Intake. Run the standard intake (target language EN by default for PSA register, aspect 9:16 fixed, duration ~30s fixed, hard no's).
  2. Reference pull. ralphy ref pull <reference-shorts-url> → frames → transcribe → analyze-video → blueprint. Save one canonical pictogram frame as the prototype --ref.
  3. Scenario lock. Write scenario.json with 10 scenes × ~3s, IF / DO-NOT / BUT / AND structure, angle "storytime" (passes the virality rubric — "analog-horror-psa" doesn't). ralphy project score <id> --strict. Get explicit user "go" on the VO lines BEFORE any generation.
  4. Style lock. ONE prototype icon on gpt-5.4-image-2 --ref <genre-frame>. Show user; get yes/no; iterate ONLY this slot until yes.
  5. Batch the 9 sibling icons via sequential bash for-loop on gemini-3-pro-image-preview --ref <approved-prototype>.
  6. Chroma-key the 10 icons to artifacts/images-keyed/.
  7. Voiceover. Verify voice exists. Sequential generation. ALL CAPS input. Loudnorm pass AFTER.
  8. SFX. Sequential. 4 climax-growl variants for layering.
  9. Music. yt-dlp pull from user-supplied YouTube synth-tension reference.
  10. Composition. HyperFrames comp with: keyed icons (transparency), MobiusWobble parent, SnowCanvas + VcrTrackingCanvas + MobiusScanlines bed, GlitchCaption (5-layer RGB-split), GlitchXIcon (5-layer RGB ghost), SignalLostColorBars climax.
  11. Render + optimize. ralphy render <id>ralphy video optimize --crf 30 --tune grain --preset veryslow.
  12. Hand off to /evaluator for the post-render quality gate.

CLI cookbook

# 4. Prototype the locked style — gpt-image with genre-ref
ralphy generate image --project <id> --slot scene-02-PROTO \
  --model openai/gpt-5.4-image-2 --size 1080x1920 \
  --ref artifacts/refs/genre-frame.jpg \
  --prompt "$(cat prompts/style-lock.txt)"
# User says "go". Save winner as artifacts/refs/icon-master.png.

# 5. Batch 9 siblings — sequential, nano-banana, ref the approved master
for slot in scene-01 scene-03 scene-04 scene-05 scene-06 scene-07 scene-08 scene-09 scene-10; do
  ralphy generate image --project <id> --slot "$slot" \
    --model google/gemini-3-pro-image-preview --size 1080x1920 \
    --ref artifacts/refs/icon-master.png \
    --prompt "$(cat prompts/$slot.txt)"
  sleep 2
done

# 6. Chroma-key to alpha
mkdir -p artifacts/images-keyed
for f in artifacts/images/*.png; do
  ffmpeg -y -i "$f" -vf "colorkey=0x000000:0.20:0.08,colorkey=0xFFFFFF:0.20:0.05,format=rgba" \
    "artifacts/images-keyed/$(basename "$f")"
done

# 7. Voice pre-flight + sequential VO
xh GET "https://api.elevenlabs.io/v1/voices/<voice-id>" \
  "xi-api-key:$ELEVENLABS_API_KEY" | jq .voice_id  # must not 404
for slot in scene-01 scene-02 ... scene-09; do
  ralphy generate voiceover --project <id> --slot "$slot" \
    --voice <voice-id> --text "$(cat prompts/vo-$slot.txt)"
done
# AFTER all VO files exist, separate loudnorm pass:
for f in artifacts/voiceover/*.mp3; do
  ralphy audio loudnorm --target -16 "$f"
done

# 8. SFX (sequential, 4 climax-growl variants for layering)
for s in vhs-hiss-bed static-pop ebs-alert-tone low-drone-bed mirror-shimmer \
         signal-lost-tone rgb-static-burst climax-growl-v1 climax-growl-v2 \
         climax-growl-v3 climax-growl-v4; do
  ralphy generate sfx --project <id> --slot "$s" --prompt "$(cat prompts/sfx-$s.txt)"
done

# 11. Render + optimize (mandatory -tune grain for noise-heavy comps)
ralphy render <id>
ralphy video optimize --project <id> --crf 30 --tune grain --preset veryslow

Minimum-viable budget: ~$1.65-2.50 / ~45-60 min (the analog-horror-fridge-001 reference ran $4.45 / 2.7× over because the style was iterated without --ref from prototype 1).

See also

  • docs/skills-vs-templates.md — why this is a skill and not a template.
  • docs/playbooks/scenarist.md — angle / virality-rubric notes (use "storytime", not "analog-horror-psa").
  • docs/playbooks/art-director.md — model picks + ref-anchor flow.
  • docs/playbooks/editor.md — HyperFrames composition for noise stacks.
  • MEMORY.md — append-only-on-generations, Kling no-music post-mix, 11labs geoblock fallback.
  • Reference postmortem: .ralphy/workspaces/<ws>/projects/analog-horror-fridge-001/POSTMORTEM.md — the 12 rules and full $ accounting this skill codifies.
  • Follow-up: scaffold templates/entertainment-viral/analog-horror-psa/ via ralphy template extract once the format is run end-to-end again. Move the noise-overlay components to src/lib/components/overlays/AnalogTV.tsx.
Install via CLI
npx skills add https://github.com/alecs5am/ralphy --skill analog-horror-psa
Repository Details
star Stars 78
call_split Forks 8
navigation Branch main
article Path SKILL.md
More from Creator