Rive Halloween Challenge · October 2026 · Play the scene

Hantu Lucu

Indonesia's scariest ghosts, made cute. Pocong and Kuntilanak haunt a misty graveyard on a malam Jumat, built with the Rive CLI, Claude Code and a WGSL GPU Canvas shader.

Pocong standing in a foggy graveyard under a full moon while Kuntilanak sits on a banyan branch

Two ghosts, one misty night

In Indonesia the scariest ghosts come from folklore, not jack-o'-lanterns. On a Friday night, the night they're said to wander, two of the most famous ones get a softer look.

Pocong

A soul wrapped in its burial shroud, tied at the head and feet, so it can only hop. Click it and it does three happy hops.

Kuntilanak

The long-haired ghost in a white dress who sits in a banyan tree, known for her "hihihi~" laugh. Click her and she giggles, and the whole night turns a spooky green.

Your cursor is a lantern. Both ghosts follow it with their eyes, and the fog pulls back where it shines.

The scene

Rendered straight from the Rive file with the CLI's headless capture.

The graveyard at rest: moon, stars, fog, Pocong on the hill and Kuntilanak on a banyan branch
At rest. Pocong stands still, both ghosts blink and watch the lantern.
Pocong in mid-hop with happy squinting eyes and big blush
Click Pocong. Three hops with squash and stretch, happy eyes and extra blush.
Kuntilanak laughing hihihi while the sky flashes green
Click Kuntilanak. "hihihi~" floats up and the shader flashes the night green.

How we built it

Claude Code drove the Rive CLI from a terminal. I gave direction; it wrote the scene, checked its own work and fixed what it saw.

  1. Read the brief

    Claude Code opened the challenge page, summarized the rules and scoring, and suggested concepts. I picked Indonesian folklore: Pocong and Kuntilanak, but cute.

  2. Install and scaffold

    It installed the CLI, ran rive create, and left rive hantu-lucu open as a live preview that rebuilds on every save.

  3. Write the scene in RML

    The whole scene is Rive Markup Language: every shape, gradient, eye and blush, built in passes from wireframe to detail. Types were looked up with rive schema instead of guessed.

  4. Add the GPU Canvas shader

    One WGSL shader draws the sky, stars, two fog layers, the lantern glow, click ripples and the scare flash, driven from a Luau script.

  5. Wire the interactions

    State machine layers for blinking, Pocong's hops and Kuntilanak's giggle; click listeners fire view model triggers; data binding moves the eyes.

  6. Test headlessly

    Every change was checked with rive --verify, rive inspect and simulated clicks like --pointer=click@330,330, comparing screenshots before and after.

  7. Hand off to the Editor

    rive push sent the project to the Rive Editor for the human-touch pass.

Rive features used

Both challenge tools, plus the rest of Rive's interactive toolkit.

Challenge tool

Rive CLI + RML

Scene authored as markup by a coding agent, previewed live, verified and pushed to the Editor.

Challenge tool

GPU Canvas + WGSL

One shader running as two layers: the sky behind the ghosts and the ground mist in front of them.

Interaction

State Machines

Three layers: Blink, Pocong (still → three hops) and Kuntilanak (float → giggle), driven by view model triggers.

Data

Data Binding

The cursor becomes view model values that move both ghosts' eyes and tilt Pocong toward the lantern.

Code

Luau Scripting

Feeds the shader its uniforms, and the giggle animation keys a script input that triggers the scare flash.

Workflow

Claude Code

The coding agent that wrote the RML, Luau and WGSL, and tested every interaction before showing it.

A peek at the fog

Two sheets of noise drift at different speeds, get thicker near the ground, and thin out where the lantern shines.

mist.wgsl

if (layer < 0.5) {
    var col = sky(px, res, t);

    // Two fog sheets drifting at different speeds, thicker near the ground.
    let f1 = fbm(warped / 140.0 + vec2f(t * 0.035, t * 0.01));
    let f2 = fbm(warped / 70.0 + vec2f(-t * 0.06, 0.0) + 11.0);
    var fog = smoothstep(0.35, 0.95, f1 * 0.65 + f2 * 0.45);
    fog *= smoothstep(0.15, 0.75, y) * 0.75;
    fog *= 1.0 - glow * 0.85;
    col = mix(col, vec3f(0.62, 0.55, 0.8), fog);

    // Lantern light: warm, flickering a little like a candle.
    let flicker = 0.85 + 0.15 * sin(t * 13.0) * sin(t * 7.3 + 1.3);
    col += vec3f(1.0, 0.62, 0.25) * glow * 0.45 * flicker;
}