Séance:以本地 Gemma 與八種已消亡的程式語言對話
Séance: a scroll of eight dead programming languages, running on local Gemma
開發者為 Hacktoberfest Week 4 挑戰打造 Séance,讓用戶透過本地 Gemma 與八種已消亡的程式語言對話。應用以 Ollama 在本機執行推理,預設模型為 gemma4:e4b,亦可改用 gemma3:4b;對話不經雲端服務,也不設資料庫。
"Well now, young one. It is good that you have found your way here,
even if I must admit with a sigh of my ancient memory that this
particular epoch seems terribly distracted from proper, sturdy
programming practice. Do come closer; we have much to discuss
regarding structure and permanence."
— COBOL, summoned via Gemma 4
For the Hacktoberfest Week 4 challenge ("Ghost in the Machine"), I built Séance — a browser-based chapel where you summon the ghost of a long-dead programming language and have a conversation with it. The ghosts are in-character: era-accurate idiom, grudges against the languages that replaced them, quiet pride in features history forgot.
Everything runs on your laptop. Nothing a ghost tells you leaves the machine. That is a design decision as much as a technical one.
Repo: github.com/RishabhVoid/seance
The chapel
The challenge prompt literally suggested "an offline séance chatbot that channels the spirit of a long-dead programming language." I took them at their word.
The whole app presents itself as a page from a 17th-century book — a single wide parchment scroll with wooden dowels at the top and bottom. There's no SaaS card grid. No round "send" button. The chapel is a catalogue: a Roman-numeral index of eight ghosts, each with a hand-drawn alchemical sigil, their birth date in Roman numerals, their cause of death in italic, and a one-line epitaph.
A ghost speaks
Click a name and the chapel calls them forth. There's a brief summoning — a candle lights, the ghost's name is spoken — and then the parchment begins to receive automatic writing: the ghost's reply types out letter by letter, starting with a blood-red drop cap, as if the page itself is being transcribed by an unseen hand.
Not a chat log. Not a bubble UI. Just text appearing on a page.
Your questions appear in typewriter type, inside a thin ruled margin, labeled — asked. The ghost replies in a Garamond body. At the bottom of the page, a single ruled line prompts: ask the dead —. No icon. No cute flame button. You press Enter.
A full exchange — asking Prolog if a cat is a mammal
The best way to understand what I built is to watch a ghost actually respond in character. I asked Prolog, "Is a cat a mammal?"
Prolog did not answer yes. It asserted facts, defined a rule for mammal(X), wrote out the query as ?- mammal(cat)., noted the unification, and then — because it is Prolog, and Prolog answers questions with questions — asked:
"Does the definition of
has_furfully capture the nature ofcat? What properties define your notion of 'animal'? ?- If these facts hold, must we not unify that Cat is a Mammal?"
That is 100% the voice of the system prompt. I wrote the character; the open-weight model did the acting.
Why open matters, specifically for this
The whole premise collapses if a cloud provider is in the loop:
- A séance should not have a per-token meter. Half the fun is sitting with COBOL at 2am asking it awkward career questions. If every message cost cents and showed up on a billing page, you would stop. Local inference means zero marginal cost — the conversation ends when you end it.
- These are private conversations. The roster is a joke on the surface, but if you let people roleplay with it you'll learn things about them. People confess to compilers. I would not want a provider logging any of this, let alone training on it.
-
The character lives in the prompt, not the model. Because each persona is a system prompt, swapping models is trivial —
gemma4:e4bby default,gemma3:4bon lighter machines. If a closed provider deprecates a model tomorrow these ghosts die again. Running local means they stay dead-but-reachable on my terms. - It works on an airplane. The point of a séance is privacy and atmosphere. "Error: offline" would ruin both.
The roster
| Ghost | Born | Interred | Voice |
|---|---|---|---|
| COBOL | 1959 | shambling since 1985 | matronly, resents OO, keeps mentioning payroll |
| APL | 1966 | ca. 1990 | elliptical, speaks in glyphs, impatient |
| Perl 4 | 1991 | 1994, when Perl 5 took the throne | gruff, sigil-obsessed, feels betrayed |
| Pascal | 1970 | the early 2000s | professorial Wirth-schooled, enjoys a semicolon |
| Lisp Machine | 1980 | 1992, with Symbolics | aristocratic, convinced the industry chose wrong |
| Algol 60 | 1960 | late 1970s | formal, the ancestor everyone forgot |
| Prolog | 1972 | exiled to the niche | Socratic, answers in clauses |
| BASIC | 1964 | late 1980s | kindly schoolteacher, remembers GOSUB fondly |
Technical shape
- Next.js 16 (App Router, Turbopack) + Tailwind v4
-
Ollama on
localhost:11434with streaming NDJSON - Default model:
gemma4:e4b(~6.6 GB); override viaOLLAMA_MODEL -
/api/chatstreams token-by-token from Ollama straight through aReadableStreamto the browser — no framework in between - The communion sheet buffers tokens and reveals characters at ~24 ms each so the reply reads as writing, not as a dump
- No database. A séance is ephemeral; banishing a ghost and re-summoning begins fresh. The dead do not keep records.
- Hand-drawn SVG for the candle (with wax drip and a flickering flame) and a unique alchemical sigil per ghost — nothing from an icon library
About 400 lines of application code. The real work was writing the eight personas as little character sketches. Each one took longer than I'd like to admit.
🌐 Community wisdom
A handful of DEV posts shaped how I thought about this:
- 🪄 natuworkguy's "Flash Onyx 2.3: I shipped a model release with zero new weights" — 40 lines of English as a model release, which is exactly the move I'm making eight times here.
- 🧵 ahmed_mahmoud360's "Streaming AI Responses in Next.js: SSE, Fetch Streams, and What Breaks in Production" — grounded my
ReadableStream-through approach. - 📜 amareswer's "Ollama API: A Practical Guide with Examples" — reference for streaming NDJSON over
/api/chat. - 🧠 kiro0x's "Why Your AI Character Keeps Breaking Under Pressure" — convinced me to put real constraints inside each persona (voice, grudges, bounds), not just a tone adjective.
Running it yourself
git clone https://github.com/RishabhVoid/seance
cd seance
pnpm install
ollama pull gemma4:e4b # or gemma3:4b for lighter machines
ollama serve & # if not already running
pnpm dev
# → http://localhost:3000
No API keys, no cloud dashboard, no account.
What I'd build next
- A "second séance" mode where two ghosts argue with each other (COBOL vs. Lisp Machine on where modern programming went wrong is the obvious first match)
- "Last words" — save a transcript as a hand-lettered scroll (SVG, print-ready)
- Expand the roster: Smalltalk-80, HyperTalk, Logo, Mercury
But the point of a Hacktoberfest week is to ship. The chapel is open; the candles are lit.
Open-weight model: Gemma 4 via Ollama · Framework: Next.js · Code: github.com/RishabhVoid/seance
Entering: Overall · Best Use of Gemma.
來源:DEV Community · dev.to



