Practice is over and it’s pouring. Dad picks you up. Then your phone buzzes, and it’s Dad.
A horror short · 4 parts · 3 endings
Dad is driving you home. Then Dad calls.
Where are you, son? I’m outside, waiting for you.
The GPS is taking you into the woods. The doors are locked. You get three seconds to decide what to do.
- 3:33 in total
- One hidden ending
- Drawn and scored in code
Watch
Start with Part 1. Then choose.
What do you do?
Each choice opens a different ending. Spoilers, so watch Part 1 first.
How it’s made
Every frame is drawn by code.
PICKUP is a single HTML file. About 1,500 lines of JavaScript draw every frame on a canvas: the rain on the windshield, the dashboard glow, the GPS screen and all 34 of the mimic’s teeth. There is no camera, no stock footage and no AI-generated art.
Every sound is generated live with the Web Audio API. The rain is filtered noise. The engine is two sawtooth waves a hair apart in pitch. Each voice is a string of short beeps, and the mimic’s beeps jump an octave at random. The four videos were rendered frame by frame from the same code.
// the mimic's grin: up to 17 teeth per row, top row hangs down, bottom row points up
const n = Math.round(lerp(5, 17, k));
for (let i = 0; i < n; i++){
const u0 = i / n, u1 = (i + 1) / n, um = (i + .5) / n;
const x0 = lerp(L, R, u0), x1 = lerp(L, R, u1), xm = lerp(L, R, um);
c.beginPath(); c.moveTo(x0, qy(u0, top, cTop) - 3); c.lineTo(x1, qy(u1, top, cTop) - 3); c.lineTo(xm, qy(um, top, cTop) + h * .38); c.closePath(); c.fill(); c.stroke();
c.beginPath(); c.moveTo(x0, qy(u0, top, cBot) + 3); c.lineTo(x1, qy(u1, top, cBot) + 3); c.lineTo(xm, qy(um, top, cBot) - h * .34); c.closePath(); c.fill(); c.stroke();
}
- Lines of JavaScript
- 1,469
- Shots
- 29
- Running time
- 3:33
- Video
- 1080 × 1920, 30 fps
- Sound effects
- 19, synthesized
- Voices
- 6, all beeps
- Endings
- 3, one hidden