haunt:別在黑暗中獨自執行這款 CLI 工具
haunt — Don't Run This CLI Tool Alone in the Dark
haunt 是一款以 Ollama + Gemma 本地模型驅動的互動式 3D 鬼魂體驗,鬼魂會在雨夜水面提燈漂浮,並與用戶對話。萬聖節模式包含 11 種逐步升級的驚嚇事件,約每 30 秒觸發一次。Ofrenda 模式供用戶悼念逝者;模型在本機運行,資料不會離開裝置。
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine
What I Built
haunt is a small, rainy, lantern-lit world with one ghost in it, and the ghost actually talks back.
You swipe up on a Halloween card, the screen flickers as if something is arriving, and a 3D ghost drifts across dark water carrying a lantern. You can steer it, push its lantern, poke it, and whisper to it. Its robe moves in the wind, the lantern swings like a pendulum, and the lantern's light breaks up on the rain-ruffled water.
I read the theme as both halves of the week:
- Halloween mode is the haunted machine. Turn on Haunting and, after at least a minute (plus a random delay), the page starts acting possessed. Each event is preceded by a build-up: the rain stops, the lantern gutters, the ghost goes still and slowly turns to face you, a heartbeat quickens, and then everything cuts to silence before it hits. Events include a fake system monitor that "scans" your screen, an idle watch ("Are you still there?"), a fake terminal, footsteps rippling across empty water, and a second pale lantern drifting toward the ghost.
- Ofrenda mode is the gentle half, for Día de los Muertos. The haunting switches off completely. The ghost asks who you're remembering and, once you name them, can speak with the warmth of that person as an echo built from your own memories. It never invents facts about them, never claims to be the real person, and says so honestly if you ask.
After every haunting event the ghost comments on what you just did ("You looked away for 40 seconds"), so the haunting feels aimed at you rather than scripted.
Demo
[Add your video link here. A good 60 to 90 second flow: the intro card, the swipe and flicker, steering the ghost, pushing the lantern, one haunting event with its build-up, then switching to Ofrenda mode.]
Code
himanshuyeolecse-jpg
/
haunt
A 3D ghost with a lantern that drifts across dark rainy water and thinks with a local AI model (Ollama + Gemma). Halloween haunting events, Ofrenda mode, ambient audio. Zero pip installs.
👻 haunt
A 3D ghost with a lantern that drifts across dark, rainy water — and thinks with a local AI model. Everything runs on your machine. Nothing leaves it.
✨ Features
- 3D ghost rendered in real-time with Three.js — cloth sway, lantern pendulum, rain ripples, and dynamic lighting
- Local AI brain — powered by Ollama + Gemma (or any model you have); whisper to it and it whispers back
- Haunting mode — 11 escalating horror events fire every ~30 seconds: fake system monitor, idle watch, terminal takeover, footsteps across the water, a second lantern, the ghost staring off-screen, and more
- Ofrenda mode — Día de los Muertos remembrance mode; the ghost listens with warmth and can speak with the echo of someone you name
- Ambient audio — synthesized rain, drone, heartbeat and whispers, plus looping background music; both start automatically
- Zero dependencies — stdlib Python only for the server…
haunt/
├── haunt_web.py # stdlib-only Python server + Ollama proxy (no pip installs)
├── src/main.js # the 3D scene (three.js): ghost, cloth, lantern, water, rain
└── static/
├── index.html # swipe-up intro + flicker transition
├── haunting.js # the haunting engine: events, sound, escalation
├── music.js # background music with ducking
├── ghost.bin # the optimized ghost model
└── app.js # bundled scene
Run it:
ollama pull gemma3
python haunt_web.py # then open http://127.0.0.1:8787
How I Built It
The open pieces
- Gemma (open-weight) runs the ghost's voice through Ollama, on localhost. The server picks any Gemma model you've pulled first, and the dropdown lets you swap models live.
- three.js and esbuild build and render the scene; meshoptimizer shrank the ghost model.
- The server is plain Python standard library. There are no CDNs, no remote fonts, no API keys.
The model and the scene
My ghost arrived as a single fused 52 MB mesh (about 1.9M triangles) with no skeleton and no animation. I decimated it to roughly 110k triangles while keeping its UVs, marked the lantern's vertices with a per-vertex weight, and animated everything in the vertex shader: the robe's hem and folds blow in the air, the lantern swings around its handle as a damped pendulum fed by the ghost's acceleration, and you can brush or click it to set it swinging. The water is a shader too: rain ripples plus the lantern's light glittering on them.
The ghost's voice
This is where the prompt design mattered most. On top of the original short, in-character lines, the ghost follows listening rules: never assume who you are or who you lost, meet laughter, tears, numbness or silence as they come, match your mood and length, avoid clichés, reply in your language, and step out of the ghost voice if someone talks about hurting themselves. In Halloween mode, the haunting events are passed to Gemma as bracketed "notes" with real facts (how long you were idle, whether you pressed Esc), and the model is told to say only what the note says.
Sound, with a lesson
All the sound is synthesized in the browser (drone, heartbeat, whispers, rain). My first mix was close to inaudible, so I added a meter and measured it: about -50 dBFS at rest. I raised the levels and added mid-range layers so laptop speakers can actually play it (about -36 dBFS at rest, around -21 dBFS at peak dread).
Honest limits
- The ghost's eyes are painted into the texture, so it can't move its eyes; it turns its whole body instead.
- "Active window" awareness is limited to the browser tab, and the system-monitor and file-scan events are simulations that use only values the browser already shares (no files are read, no commands run, the camera is switched off the instant it turns on).
Why Does Open Innovation Matter?
This project is about trust, and a closed API would have undermined it.
- Private grief. In Ofrenda mode, people may type the name of someone they've lost. With a local open-weight model, those words never leave the machine. That wasn't a cost-saving choice; it's the only version I'd be comfortable asking someone to use.
- Offline. The whole experience runs without internet once the model is pulled: three.js, textures, music and fonts all ship in the folder.
- Changing the ghost's mind. Swapping models is one dropdown, and the personality lives in a prompt I can read, edit and test, not behind someone else's policy changes. I could add safety behavior (stepping out of character when someone sounds unsafe) at the exact layer where it belongs.
- Free to run. Nothing is metered, so the ghost can keep talking for as long as someone wants to sit with it.
Prize Categories
The Spirit of ItIt clicked at 2:30 a.m. while test-running the code in a pitch-black room with headphones on. I typed a target name into the prompt, hit enter, and watched the terminal drop into a dead silence before the ambient audio shift kicked in. The sudden, low-frequency hum paired with the slow-scrolling text made me glance over my shoulder at the dark hallway behind my desk. That split-second of genuine hesitation—wondering if I really wanted to see what keypress triggered next—was when I knew the atmosphere was dialed in right.
Credits and assets
Prize CategoriesGrand Prize / Spookiest Project: For building an immersive, interactive terminal/CLI experience focused on atmospheric horror and suspense.Best Maintainer / Open Source Integration: Fully open-sourced repository with clean documentation, clear installation steps, and open setup for Hacktoberfest contributors.
Best Use of ElevenLabs: Adding atmospheric audio elements, creepily realistic voice narration, and haunting soundscapes to elevate the terminal horror experience.
Best Use of GitHub Copilot: Built using GitHub Copilot CLI and coding agent integrations to streamline open-source contributions and maintainer workflows.
Best Use of Backboard: Integrating Backboard’s open-source terminal coding capabilities and RAG memory to drive contextual suspense and dark interactive story paths.
Credits and assets
- Music: "Nebula Voyager" by OneCinematicStudio https://pixabay.com/music/orchestral-nebula-voyager-epic-space-amp-cosmic-cinematic-music1-529058/
- Rain-and-lantern look inspired by credit - Pixlora https://in.pinterest.com/pin/921197298821103157/
來源:DEV Community · dev.to

