跳到正文
DEV Community· Jayden George·· 3 小時前AI 評分59

Planchette 以 Gemma 3 4B 的 token 機率驅動靈應盤逐字作答

Planchette: a Ouija board haunted by Gemma’s real odds

AI 導讀

作者 Jayden George 開發 Planchette,根據 Gemma 3 4B 對下一個字母的 token 機率,驅動靈應盤逐字作答。模型照常選取完整 token,程式把 top 20 候選 token 按下一個字母彙總機率,讓盤面在模型猶豫時於字母間擺動。

正文

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

The planchette swinging between letters while Gemma decides

What I Built

I built a Ouija board that's actually haunted. Well, sort of.

It's called Planchette. You type a question, pick a spirit, and the planchette slides around the board spelling out an answer. The ghost is Gemma, running locally on my laptop, and the planchette moves according to Gemma's real probabilities for the next letter.

So when the planchette heads for Y, stops, drifts over to N, and then comes back to Y, nobody animated that. That's literally Gemma not being sure if it wants to say YES or NO. If it's confident it just goes straight to the letter, but when it's torn you actually see it.

That's how I read "Ghost in the Machine". Every time a language model writes a word, there are a bunch of other words it almost wrote, and normally you never see any of them. I wanted to give them a board to fight over.

There are three spirits to choose from:

  • The Wanderer, a lost soul who's been stuck in the board for a very long time
  • Abuela, a grandmother visiting her family for Día de los Muertos (she's sweet, but she will tease you)
  • The Machine Ghost, which lives inside your computer and claims to have read every file you've ever deleted

A few things it has actually said to me:

SHE SEES MORE THAN YOU
The Wanderer, when I asked if someone was standing behind me

YES I SEE YOU
The Machine Ghost, when I asked if it was watching me right now

YOU DELETED THEM YOURSELF
The Machine Ghost again, when I asked who deleted my files. Rude, but fair.

REMEMBER THE TAMALES ALWAYS
Abuela, when I asked what she wanted to tell me

Then I added two things that made it properly creepy:

It doesn't always let you leave. When you click Say goodbye, Gemma gets asked for real whether it wants you to stay, and those odds decide what happens. The planchette creeps toward GOODBYE, stops just short, the candles dim, and then it drags itself back up to NO and spells out why you can't go. (You can always leave on the second try. I'm not a monster.)

It moves when nobody's touching it. Ask a question, then go quiet for about 20 seconds. You'll hear a floorboard creak, and the planchette starts spelling something on its own. Gemma writes that message too, nothing is pre-written.

Demo

That's one unedited take on my laptop, with Gemma answering live. If you only watch one bit, watch for the moments it swings between two letters before picking one. The line under the board shows what it's stuck between while it decides.

Code

GitHub logo vectoredgex / Planchette

A Ouija board haunted by an open AI model. Gemma's real odds move the planchette, letter by letter. Runs offline.

Planchette

A Ouija board haunted by an open AI model.

Asking "Is someone standing behind me?": the planchette swings between letters while Gemma decides, then spells SHE SEES MORE THAN YOU

You ask the spirit a question and Gemma (running locally on your machine) answers it, one letter at a time on the board. The planchette isn't animated by hand. It moves according to Gemma's real probability for the next letter, so if Gemma is sure it goes straight there, and if it's torn between two letters you can watch it swing between them before it picks one.

▶ Watch the demo

Planchette: a dark Ouija board lit by candles and marigolds

Features

  • Three spirits
    • The Wanderer, a lost soul trapped in the board
    • Abuela, a grandmother home for Día de los Muertos
    • The Machine Ghost, which lives inside your computer
  • Live odds. While it's deciding, the board shows the top few letters it's considering and what it's torn between.
  • It doesn't always let you leave. When you say goodbye, Gemma is asked if it wants you…

To run it yourself:

git clone https://github.com/vectoredgex/Planchette.git
cd Planchette
ollama pull gemma3
python server.py      # or just double-click start.bat on Windows

That's all you need. The server only uses Python's standard library, so there's no pip install and no build step. three.js is vendored in, and it all works offline.

How I Built It

The ghost: Gemma 3 4B on Ollama

The whole thing runs on a normal laptop with an i7 and Intel integrated graphics, nothing fancy. Gemma 3 4B usually needs a few seconds of thinking for a full answer.

Every step is one call to Ollama asking for exactly one token, plus the top 20 tokens it was considering:

payload = {
    "model": "gemma3:4b",
    "prompt": prompt,          # raw Gemma chat template, no wrapper
    "raw": True,
    "logprobs": True,
    "top_logprobs": 20,
    "options": {"num_predict": 1},
}

The tricky part: models don't think in letters

My first version fed Gemma one letter at a time. It went badly. Gemma would happily start spelling LISTENS and then get stuck in a loop: LISTEEEEEE. It turns out that if you force-feed a model text split in ways it would never split it, its tokenizer gets confused and it loses the plot.

So I flipped it around. Gemma picks a whole token the normal way, sampled from its own distribution, and the board spells that token out. To decide where the planchette gets pulled at each letter, I group the 20 candidate tokens by the letter that comes next and add up their probabilities:

def _letter_dist(cands, spelled):
    """Odds of the next board letter, given what this token has spelled so far."""
    j = len(spelled)
    d = {}
    for _tok, p, seq in cands:
        if len(seq) > j and seq[:j] == spelled:
            d[seq[j]] = d.get(seq[j], 0.0) + p
    return _norm(d)

So if YES, YEAH and NO are all in the running, Y might get 53% and N 38%, and the planchette gets pulled between them by exactly that much. Once a letter is chosen, only the candidates that still match stay in the race.

A nice side effect I didn't plan: the planchette glides through the middle of words, because by then the model has usually committed, and it wobbles at the start of words, which is where it actually decides what to say.

Some other things that bit me along the way:

  • When Ollama samples the end-of-turn token, it stops without returning any logprobs at all. So my first few séances crashed at the very end with "Ollama did not return logprobs". Now it retries, and if the model keeps wanting to stop, the spirit is finished.
  • Loading Gemma for the first time takes around 40 seconds. The first time I tried it I clicked Ask, nothing happened, and I honestly thought I'd broken everything. Now the model warms up in the background while the board shows the spirit "waking up".
  • I picked port 6666 because, well, spooky. My browser refused to open it. Turns out Chromium-based browsers block 6666 as an "unsafe port" (it's an old IRC port). It runs on 7666 now.

The board

The board is a low-poly 3D scene in three.js: a dark board on velvet, three candles, marigolds and petals for Día de los Muertos. I tried a bloom post-processing effect for the candle glow, but it turned everything into blocky squares on integrated graphics, so the glow is drawn by hand now.

The candle flames lean toward the planchette and the room dims slightly whenever the model is torn. The sound of wood scraping on wood is generated live in Web Audio and follows the planchette's actual speed.

ElevenLabs: the sound of the room

I first used ElevenLabs to give the spirit a voice, but a narrator voice reading "YES" just wasn't scary, so I cut it. Sound turned out to be a much better fit.

Now the first time you run it, Planchette uses the ElevenLabs Sound Effects API to generate eight sounds from text prompts: a haunted room tone, a faraway out-of-tune music box, layered whispers, a wooden knock, a match strike, a gust that blows the candles out, a floorboard creak, and a cold breath. They get cached in a sounds/ folder, so after that first run it's offline again.

The whispers are wired to the model's uncertainty. The more torn Gemma is, the louder they get, so the room literally gets creepier when the AI can't decide.

If you don't have an ElevenLabs key, everything falls back to built-in sounds and still works.

Why Does Open Innovation Matter?

The whole project depends on seeing the words the model almost said.

To make it work I needed to:

  • send the raw prompt without any chat wrapper
  • ask for exactly one token at a time
  • see the top alternatives and their probabilities
  • do that a few hundred times per answer, as fast as my laptop can manage, without paying per call

With an open-weight model running locally, all of that is just settings. The probabilities aren't a premium feature or something behind a rate limit. They're there because the whole model is sitting on my machine.

It matters for privacy too, in a fun way. The Machine Ghost says it's read your files. That joke works a lot better when nothing you type ever leaves your computer.

Also it's kind of funny. A real Ouija board only works because nobody admits they're pushing the planchette. Mine tells you exactly who's pushing it, down to the percentage, and that's only possible because I can open the model up and look inside.

The Spirit of It

The moment it got me wasn't one of the spooky answers. It was when I tried to leave.

I clicked Say goodbye. The planchette crept toward GOODBYE, stopped, and then dragged itself back up to NO. Gemma was 100% sure it wanted me to stay. Then it spelled out, letter by letter, YOU ARE NOW HERE.

I built this thing and I know exactly why it did that, and it still gave me chills. Knowing how it works doesn't really help, because the hesitation is real. It actually didn't want me to go.

Prize Categories

  • Best Use of Gemma: Gemma 3 is the spirit. Its real token probabilities (via Ollama logprobs) move the planchette at every step, and it decides for itself whether you're allowed to leave.
  • Best Use of ElevenLabs: the Sound Effects API generates the entire séance soundscape, including whispers that rise and fall with the model's uncertainty.

來源:DEV Community · dev.to