{
  "schemaVersion": 1,
  "id": "vhs-camcorder",
  "version": "1.0.0",
  "status": "published",
  "kind": "effect",
  "title": "VHS Camcorder Look (1987 Home Video)",
  "summary": "Makes any animation look like a 1980s camcorder tape: soft picture, bleeding colour, tracking noise, a shaky hand and the red-orange date stamp in the corner.",
  "description": "The home-video look behind this autumn's 80s AI trend. Everything you draw is put through a simulated VHS tape: detail goes soft, colour smears to the right of bright things, highlights trail, lines wobble, a band of tracking noise rolls up the frame, white dropouts flick past and the bottom edge tears where the video heads switch. On top comes the camcorder's own display, burned into the picture: a blocky dot-matrix date and time, a blinking REC dot or PLAY and a tape counter. A helper adds the camcorder operator too: handheld drift, a slow zoom creep and the autofocus hunting once. Type your own date, pick the colour cast (warm tungsten, natural or faded old tape) and how worn the tape is. The demo films a spring-break sunset on the beach or a birthday cake with its candles lit; the same code degrades your own canvas, 3D scene or video frame.",
  "role": "Full-frame finishing effect (Canvas 2D) plus a camcorder date-stamp overlay and handheld camera move.",
  "collections": [
    "backgrounds",
    "kinetic-text"
  ],
  "tags": [
    "vhs",
    "camcorder",
    "80s",
    "1980s",
    "1987",
    "retro",
    "home video",
    "date stamp",
    "tracking",
    "analog",
    "tape",
    "lo-fi",
    "nostalgia",
    "glitch",
    "chroma bleed",
    "handheld",
    "2d",
    "canvas"
  ],
  "synonyms": [
    "vhs effect",
    "vhs filter",
    "vhs overlay",
    "camcorder effect",
    "camcorder date stamp",
    "80s home video",
    "80s filter",
    "1980s AI trend",
    "80s AI photo trend video",
    "retro tape look",
    "analog video effect",
    "old home movie look",
    "found footage look",
    "tracking lines",
    "rec overlay",
    "date stamp overlay"
  ],
  "useCases": [
    "The 80s AI photo trend as a video ('A day in 1987')",
    "Throwback or nostalgia reels",
    "Music video or album teaser with a found-footage feel",
    "Retro product ad or brand birthday post"
  ],
  "featured": null,
  "publishedAt": "2026-10-04",
  "implementation": {
    "target": "canvas2d",
    "runtime": "browser",
    "language": "javascript",
    "module": "esm",
    "entry": "src/vhs-camcorder.js",
    "exports": [
      {
        "name": "createVHS",
        "signature": "createVHS({ softness?, bleed?, sharpen?, smear?, noise?, wear?, headSwitch?, scanlines?, look?, seed? }) => { apply(ctx, width, height, info) }",
        "description": "The tape. apply() reads the 2D canvas, degrades it in place (soft luma, bleeding chroma, wobble, tracking band, dropouts, head-switch tear, colour cast) and writes it back."
      },
      {
        "name": "drawCamcorderOSD",
        "signature": "drawCamcorderOSD(ctx, width, height, info, { date?, clock?, mode?, blinkDate?, color?, scale?, counterStart? })",
        "description": "The camcorder display: dot-matrix date and time bottom right, REC (blinking dot) or PLAY with a tape counter top left. Draw before apply() so it is burned in."
      },
      {
        "name": "camcorderMove",
        "signature": "camcorderMove(info, { shake?, zoomCreep?, focusHunt?, focusAt?, size?, seed? }) => { x, y, angle, zoom, focusBlur }",
        "description": "Handheld drift and tremor, a slow zoom creep and one autofocus hunt, as numbers to apply to your scene."
      },
      {
        "name": "drawDotText",
        "signature": "drawDotText(ctx, text, x, y, { dot?, color?, shadow?, tracking? }) => width",
        "description": "Draws any text in the built-in 5×7 dot-matrix camcorder font (A–Z, 0–9, : . - / ' !)."
      }
    ],
    "reusable": [
      "src/vhs-camcorder.js"
    ],
    "demo": "demo.js",
    "harness": "index.html"
  },
  "dependencies": [
    {
      "id": "apw-runtime",
      "version": "1.0.0",
      "scope": "demo",
      "reason": "Demo harness and seeded random numbers. src/vhs-camcorder.js has no dependencies."
    }
  ],
  "params": [
    {
      "key": "scene",
      "label": "Demo picture",
      "help": "What the demo films. Your own project can put anything through the tape.",
      "type": "select",
      "default": "beach",
      "options": [
        {
          "value": "beach",
          "label": "Spring-break sunset"
        },
        {
          "value": "party",
          "label": "Birthday candles"
        }
      ]
    },
    {
      "key": "date",
      "label": "Date stamp",
      "help": "Shown bottom right in the camcorder font. Letters, numbers, spaces and : . - / ' ! work.",
      "type": "text",
      "default": "JUL 12 1987",
      "maxLength": 16
    },
    {
      "key": "clock",
      "label": "Time stamp",
      "help": "Shown above the date. Leave empty to hide it.",
      "type": "text",
      "default": "6:48 PM",
      "maxLength": 12
    },
    {
      "key": "mode",
      "label": "Camera display",
      "type": "select",
      "default": "rec",
      "options": [
        {
          "value": "rec",
          "label": "Recording (REC)"
        },
        {
          "value": "play",
          "label": "Playback (PLAY + counter)"
        },
        {
          "value": "none",
          "label": "Date only"
        }
      ]
    },
    {
      "key": "blinkDate",
      "label": "Blink the date",
      "type": "boolean",
      "default": true
    },
    {
      "key": "stampColor",
      "label": "Date stamp colour",
      "type": "color",
      "default": "#ff6a1f"
    },
    {
      "key": "look",
      "label": "Colour cast",
      "type": "select",
      "default": "warm",
      "options": [
        {
          "value": "warm",
          "label": "Warm tungsten"
        },
        {
          "value": "natural",
          "label": "Natural"
        },
        {
          "value": "faded",
          "label": "Faded old tape"
        }
      ]
    },
    {
      "key": "wear",
      "label": "Tape wear",
      "help": "Wobble, rolling tracking noise and white dropouts. 0 is a fresh tape.",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.35
    },
    {
      "key": "bleed",
      "label": "Colour bleed",
      "help": "How far colour smears to the right of bright things.",
      "type": "number",
      "min": 0,
      "max": 1.5,
      "step": 0.05,
      "default": 0.8
    },
    {
      "key": "softness",
      "label": "Softness",
      "help": "How much fine detail the tape loses.",
      "type": "number",
      "min": 0,
      "max": 1.5,
      "step": 0.05,
      "default": 0.8
    },
    {
      "key": "noise",
      "label": "Grain",
      "type": "number",
      "min": 0,
      "max": 1,
      "step": 0.05,
      "default": 0.4
    },
    {
      "key": "headSwitch",
      "label": "Torn bottom edge",
      "help": "The noisy strip where the video heads switch.",
      "type": "boolean",
      "default": true
    },
    {
      "key": "shake",
      "label": "Handheld shake",
      "type": "number",
      "min": 0,
      "max": 2,
      "step": 0.05,
      "default": 1
    },
    {
      "key": "zoomCreep",
      "label": "Zoom creep",
      "help": "How far the camera slowly zooms in over the clip.",
      "type": "number",
      "min": 0,
      "max": 0.3,
      "step": 0.01,
      "default": 0.08
    },
    {
      "key": "focusHunt",
      "label": "Autofocus hunts",
      "help": "The picture blurs and snaps back sharp once near the start.",
      "type": "boolean",
      "default": true
    },
    {
      "key": "seed",
      "label": "Variation",
      "help": "Changes the shake, the noise and the details of the demo picture.",
      "type": "integer",
      "min": 1,
      "max": 9999,
      "step": 1,
      "default": 3
    }
  ],
  "presets": [
    {
      "id": "default",
      "label": "Spring break '87",
      "params": {}
    },
    {
      "id": "birthday",
      "label": "Birthday tape",
      "params": {
        "scene": "party",
        "date": "MAR 03 1988",
        "clock": "8:12 PM",
        "blinkDate": false,
        "wear": 0.25,
        "bleed": 1,
        "noise": 0.55,
        "shake": 0.8,
        "zoomCreep": 0.12
      }
    },
    {
      "id": "worn",
      "label": "Worn-out tape",
      "params": {
        "look": "faded",
        "mode": "play",
        "date": "AUG 21 1986",
        "clock": "",
        "blinkDate": false,
        "wear": 0.85,
        "bleed": 1.25,
        "softness": 1.15,
        "noise": 0.7,
        "shake": 0.6,
        "zoomCreep": 0.02,
        "focusHunt": false
      }
    }
  ],
  "output": {
    "width": 1080,
    "height": 1080,
    "fps": 30,
    "durationSeconds": 8,
    "posterSeconds": 2.5,
    "alpha": false,
    "motion": "procedural",
    "loop": {
      "seamless": false,
      "method": "A continuous handheld take with a zoom creep; it does not loop."
    },
    "seed": {
      "param": "seed",
      "behavior": "Seed sets the handheld shake, every noise pattern, the tracking band's start and the demo's clouds, glints, lights and sprinkles. Same seed, same frames."
    }
  },
  "composition": {
    "inputs": "Canvas 2D context, width, height and the frame info ({ frame, time, duration }). apply() takes whatever is already drawn on the canvas.",
    "outputs": "apply() replaces the frame with the taped version (opaque). drawCamcorderOSD draws on top without clearing.",
    "coordinates": "Pixels. Effect sizes are tuned at 1080 wide and scale with the canvas. The display sits 7.5% in from the edges.",
    "lighting": "None of its own. The colour cast (warm, natural, faded) is applied after decoding the tape.",
    "timing": "Pure function of info.frame and info.time: noise changes every frame, the tracking band drifts up, REC blinks once a second.",
    "compatibleWith": [
      "kinetic-title",
      "stop-motion-feel",
      "gradient-background",
      "orbit-camera",
      "chrome-orbit"
    ],
    "notes": [
      "Draw the date stamp before apply() so it softens and bleeds with the picture, as a real camcorder burned it into the tape. Draw it after apply() for a crisp overlay.",
      "apply() reads pixels back, so the canvas must be same-origin (no tainted canvas). For three.js, draw the WebGL canvas onto a 2D canvas first, then apply.",
      "The date and time are plain text you choose; nothing reads the real clock, so renders stay repeatable."
    ]
  },
  "reuse": [
    "Make the 80s AI photo trend move: draw your restyled 1985 portrait (or each frame of the AI clip) on a canvas, let camcorderMove drift and zoom it, add drawCamcorderOSD with your own date, then apply().",
    "Put Kinetic Title Reveal through the tape for a retro title card or end slate, with mode 'play' and the counter running.",
    "Combine with Stop-Motion Feel for a home-made claymation clip that looks like it was found on an old cassette."
  ],
  "integration": {
    "newProject": "Create the tape once with createVHS(options). Each frame: draw your scene (optionally transformed by camcorderMove(info)), call drawCamcorderOSD(ctx, width, height, info, { date }), then vhs.apply(ctx, width, height, info).",
    "existingProject": "Copy src/vhs-camcorder.js. At the end of your 2D frame call vhs.apply(ctx, width, height, info); add drawCamcorderOSD just before it for the date stamp. For WebGL, copy the WebGL canvas onto a 2D canvas each frame and apply there."
  },
  "performance": {
    "notes": "One pass over every pixel with a few running-sum blurs per line, plus the demo's own drawing: about 0.3–0.5 s a frame at 1080×1080 in headless Chrome on a CPU-only render machine. Fine for offline renders; live previews play at a few frames a second.",
    "limits": "Cost grows with pixel count. For live use, render at 540 or 720 and scale up (VHS was never sharp anyway); at 4K expect several seconds a frame."
  },
  "limitations": [
    "It degrades what it is given: it cannot add 80s hair, clothes or rooms to footage. Pair it with an AI restyle for that.",
    "The display font covers capital letters, digits and a few symbols only; there are no accented letters.",
    "Square and widescreen frames are fine, but it does not crop to the 4:3 shape of real tape."
  ],
  "license": {
    "spdx": "MIT",
    "notice": "Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.",
    "thirdParty": []
  },
  "provenance": "Original code written for AI Pixel Wizard on 2026-10-04 by an automated agent (the daily trend routine), inspired by the 80s AI photo trend and its 'A Day in 1987' VHS home-video versions (see content/trends/2026-10-04-vhs-camcorder.md). The list of tells to reproduce (blinking red-orange date stamp lower right, chroma bleed, tracking noise along the bottom, handheld shake, zoom creep, autofocus hunting) follows https://www.getstarrd.app/blog/how-to-make-80s-ai-video. Separating brightness and colour (YIQ) and blurring colour more than brightness is the standard description of how VHS records video; the code, the dot-matrix font and the demo pictures were all written for this package. No code, images, fonts or shaders were copied.",
  "changelog": [
    {
      "version": "1.0.0",
      "date": "2026-10-04",
      "notes": "Initial release."
    }
  ]
}
