THE VEIL β I Built an AI Ouija Board That Runs Entirely on Your Machine π―οΈ
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine What I Built THE VEIL is an AI-powered Ouija board that runs entirely on your local machine β no cloud, no API
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine
What I Built
THE VEIL is an AI-powered Ouija board that runs entirely on your local machine β no cloud, no API keys, no internet required once you've pulled the model.
You ask a question, and an ancient spirit answers. But not like a chatbot. The planchette physically glides across the board, pausing over each letter, illuminating it, and spelling out a cryptic response one character at a time. When the message is complete, it drifts to GOODBYE.
The theme said Ghost in the Machine. I took it literally: there's an LLM living inside Ollama on your laptop, pretending to be a spirit trapped behind a 19th-century parlor game. It doesn't know it's software. It speaks in fragments. It never explains itself.
The inspiration was simple. It's October 31st. What if a sΓ©ance actually worked β and the spirit on the other side was a 3-billion-parameter language model running on localhost?
Demo
The experience flows like this:
Landing β A dark screen. "THE VEIL" in gold gothic lettering. Floating particles. Some questions are better left unanswered. Click ENTER THE SΓANCE.
The Board β A full CSS/SVG Ouija board fills the screen: curved alphabet rows (AβM, NβZ), numbers 0β9, YES and NO in the corners, GOODBYE at the bottom. Flickering candles at the edges. A film grain overlay. A wooden planchette with a circular viewing window hovers at center.
Ask β Type your question. Click SUMMON. The screen dims. The planchette trembles. Reaching beyond the veil...
The Response β The planchette begins to move. It glides to the first letter β the letter glows gold β the character appears in the transcript below. Then the next. And the next. Each movement is spring-animated using actual DOM coordinates, so it's pixel-accurate at any screen size.
Goodbye β When the message is complete, the planchette drifts to GOODBYE. You can ask again or end the sΓ©ance.
The whole thing runs at localhost:5173 (frontend) and localhost:8000 (backend).
Code
https://github.com/radhikaaa25/the-veil
How I Built It
The Stack
| Layer | Technology | Why |
|---|---|---|
| Frontend | React + Vite | Fast dev, hot reload |
| Styling | Tailwind CSS v4 + vanilla CSS | Design tokens via @theme, plus detailed component styles |
| Animation | Framer Motion | Spring physics for the planchette, useMotionValue + useSpring for natural gliding |
| Backend | Python + FastAPI | Thin bridge between frontend and Ollama |
| AI | Ollama + llama3.2:3b
|
Fully local, open-weight, no API key |
| Audio | Web Audio API | Ambient drone generated from oscillators β no external sound files |
The Open-Source AI
The AI is Ollama running llama3.2:3b (Meta's open-weight model, 3 billion parameters). The backend sends each question to Ollama's chat API with this system prompt:
You are an ancient, mysterious spirit communicating through a Ouija board. Respond with a short, eerie, poetic, and cryptic message. Keep responses under 15 words. Never explain yourself. Avoid modern slang, emojis, or references to being an AI.
The model stays in character remarkably well at this size. Short, constrained prompts are where small open-weight models shine β you don't need GPT-4 to say "THE ANSWER LIES BENEATH WHAT YOU HAVE FORGOTTEN" in a creepy voice.
The response is sanitized server-side: stripped to uppercase, punctuation removed, collapsed to only characters that exist on the board (AβZ, 0β9, spaces). The frontend never has to guess whether a character is renderable.
The Planchette Animation
This was the hardest and most important part. The planchette doesn't use a typing effect. It physically moves.
Every letter on the board is a React ref. When the AI response arrives, the app:
- Splits the response into individual characters
- For each character, calls
getBoundingClientRect()on that letter's DOM element - Calculates the position relative to the board container
- Sets a Framer Motion spring target (
stiffness: 80, damping: 22, mass: 0.8) - The planchette glides to the letter with natural deceleration
- The letter illuminates with a gold glow
- The character appends to the revealed text below the board
- After all characters: the planchette moves to GOODBYE
Because positions are read from the DOM at animation time, the board is fully responsive. Resize the window and the planchette still lands exactly on each letter.
The Atmosphere
The board itself is pure CSS β layered gradients that simulate dark wood grain, a double border for the antique frame effect, and an inner box-shadow for depth. Typography uses Cinzel Decorative (for titles) and IM Fell English (for body text), both loaded from Google Fonts.
The particles are a <canvas> element with 40 floating golden dots that drift upward and fade. The candles are CSS animations (@keyframes flicker) with a radial gradient glow. The ambient sound is generated entirely from the Web Audio API β three sine-wave oscillators (55Hz, 220Hz, 36.7Hz) with LFO modulation for a slow, eerie drone. No audio files ship with the project.
Architecture
React (Vite :5173)
β POST /api/ask {question}
FastAPI (:8000)
β Ollama chat API
llama3.2:3b (localhost:11434)
β response
FastAPI β sanitize β {answer}
β
React β split chars β animate planchette β reveal text
The backend is 2 files. The frontend is 9 components. No database, no auth, no cloud.
Why Does Open Innovation Matter?
This project only works because the AI is open and local.
It works with the lights off. Once you've pulled llama3.2:3b from Ollama, the entire sΓ©ance runs without internet. The model sits on your disk. The backend talks to localhost. The frontend talks to the backend. No tokens, no rate limits, no "service unavailable."
The persona stays in character. With a closed API, you're fighting content filters. "You are a spirit communicating through a Ouija board" is exactly the kind of prompt that triggers safety guardrails on commercial models. A local open-weight model doesn't second-guess the fiction. It plays the role.
It costs nothing to run. A 3B parameter model runs comfortably on a laptop CPU. No GPU required. No billing page. No surprise invoice because your Halloween party guests asked it 200 questions.
It's private. Your questions to the spirit board stay on your machine. Nobody's logging "will I find love" or "is someone watching me" to a server in Virginia.
The model is swappable. Don't like the responses? Change OLLAMA_MODEL in .env to mistral, phi3, gemma2, or any model Ollama supports. The prompt engineering is simple enough that most models handle it well.
A closed API could technically generate the text. But it couldn't do it offline, for free, without filters, on a Halloween night with the Wi-Fi turned off for atmosphere. Open made this the experience it is.
The Spirit of It
I built this on Halloween. That wasn't an accident.
There's something about a Ouija board that a chatbot window can never replicate β the tension of watching the planchette move, letter by letter, not knowing where it's going. The pause between characters. The moment you realize the word it's spelling.
When I first got the planchette animation working and asked it "Is someone watching me?", it spelled out:
THE SHADOWS REMEMBER YOUR NAME
I was alone in my room at midnight. The candles were flickering on screen. The ambient drone was humming through my headphones. And for about three seconds, I forgot it was a language model.
That's the moment this project is for.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.