Ghostlight: a ghost that refuses to lie

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine Ghostlight: a ghost that refuses to lie What I Built Ghostlight is a remembrance studio that runs entirely in your browser. You answer six small questions about someone you carry — the kitchen, the song, the place, the saying, the small thing, the unfinished thing — and it turns your words into three things: A remembrance. A short, concrete portrait written from your answers. An ofrenda. A tiered altar of six candles, marigolds, water, cut paper and the offerings hiding in your answers, inspired by Día de Muertos. Click a lit candle or an offering to read the memory behind it. A keepsake. A single HTML file with the portrait, the remembrance and every memory inside. It opens offline, needs no app, and contains no scripts. Everything runs on your own machine. An open-weight Gemma 2 model writes the remembrance through WebGPU, and the archive lives in IndexedDB

Ghostlight: a ghost that refuses to lie

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

Ghostlight: a ghost that refuses to lie

The finished ofrenda: six candles, a portrait, marigolds, water, and the offerings from the answers

What I Built

Ghostlight is a remembrance studio that runs entirely in your browser. You answer six small questions about someone you carry — the kitchen, the song, the place, the saying, the small thing, the unfinished thing — and it turns your words into three things:

  1. A remembrance. A short, concrete portrait written from your answers.
  2. An ofrenda. A tiered altar of six candles, marigolds, water, cut paper and the offerings hiding in your answers, inspired by Día de Muertos. Click a lit candle or an offering to read the memory behind it.
  3. A keepsake. A single HTML file with the portrait, the remembrance and every memory inside. It opens offline, needs no app, and contains no scripts.

Everything runs on your own machine. An open-weight Gemma 2 model writes the remembrance through WebGPU, and the archive lives in IndexedDB. There is no backend, no account, no analytics, and no cloud inference. Uploaded: zero bytes.

I read "Ghost in the Machine" two ways, and both are in the build.

The first is the traditional one: this is a tool for the dead, built on the nights when the veil is supposed to be thin. Día de Muertos is not a séance; it is a table set for the people who can't come to dinner. I borrowed the shape of that table — tiers, candles, cempasúchil, a glass of water for the journey — with as much respect as I could, and I explain every element in plain language on the page so nobody has to already know the tradition to understand the room.

The second reading is the one that decided the architecture: the ghost is not the model. It is the archive. The model is just the candle that makes it visible. Most "AI memory" projects put the model in the middle, so when the company changes the model, the price, or the policy, the memory goes with it. Ghostlight inverts that. The archive is the source of truth, the model is small and replaceable, and there is a mode that keeps your words exactly as you wrote them, with no model at all.

That last part matters more than any feature list, so the tagline became a rule:

The ghost is not the model. The model is the candle. The ghost is what you remember.

The demo archive in these screenshots is fictional. Ruma Das is not a real person: the portrait was generated for the project, the six memories were written by me, and the finished demo remembrance is a hand-written target rather than a model output. Your archive never leaves your browser.

Demo

Live demo: [your Render URL here — the repo ships a one-click render.yaml blueprint, see Code below]

If you'd rather not click a link, the whole thing runs locally in about a minute:

git clone https://github.com/soumwadeep/ghostlight.git
cd ghostlight
npm install
npm run dev

Then open http://localhost:5173 and choose one of two honest options on the weave screen:

  • Summon Gemma 2 · ~1.4 GB. Runs in the tab through WebGPU, downloaded once, then cached. After that the room works with the Wi-Fi off.
  • Candle mode. No download, no model, instant. It arranges your own sentences into the remembrance and never rewrites them.

There is also a 60-second video demo you can record yourself if you prefer (four shots are enough): walk through the finished demo ofrenda, click a candle to read a memory, ask a question it can answer, then ask one it can't — and watch the flame refuse.

Code

GitHub logo soumwadeep / ghostlight

A local-first remembrance studio for Día de Muertos. Gemma 2 runs entirely in your browser; your memories never leave your device. Built for Hacktoberfest 2026 — Ghost in the Machine.

Ghostlight

Keep a light on for the stories that made you.


A local-first remembrance studio for Día de Muertos.

Six questions → a remembrance written by Gemma 2 in your browser → an ofrenda
you can ask, keep, and carry offline


licence MIT
no backend
model Gemma 2 · 2B
offline PWA
Hacktoberfest 2026

Ghostlight — a local-first remembrance studio

What this is

Ghostlight asks six small questions about someone you carry — a dish, a song, a place, a saying, a small thing, an unfinished thing — and turns the answers into three things:

  1. A remembrance. A short, concrete portrait written from your words.
  2. An ofrenda. A tiered altar of six candles, marigolds, water and the offerings hiding in your answers, inspired by Día de Muertos. Click a lit candle to read its memory.
  3. A keepsake. A single HTML file that opens offline, needs no app, and contains no scripts.

Everything happens in your browser. An open-weight Gemma 2 (2B, ~1.4 GB, downloaded once) runs through WebGPU via…

The parts worth reading:

MIT licensed. React 19, Vite 8, TypeScript, motion, idb-keyval, vite-plugin-pwa, and no backend of any kind.

How I Built It

The open-source AI is not decoration here. It is the only way the project's promise can be true.

The stack:

Piece What it does
Gemma 2 · 2B IT (open weights, Google DeepMind) Writes the remembrance and rephrases grounded answers
WebLLM (Apache-2.0, MLC AI) Runs the model in the browser via WebGPU
Local inference The model, the archive, the photograph, and your questions never leave the device
IndexedDB The archive. No database service, no auth provider, no API key
My own pipeline No agent framework: a small, auditable, hand-written path from answers to a remembrance

The data flow is deliberately boring, because boring is what you want when the data is someone's grief:

your six answers
   → the archive            (IndexedDB, on this device)
   → memory search          (the archive decides what is true)
   → Gemma 2 · 2B           (WebLLM + WebGPU, in the tab)
   → a remembrance          (150–200 words, three paragraphs)
   → the ofrenda + a keepsake

There are two AI jobs, with different rules, and the difference is the whole point.

Weaving is creative but constrained. The prompt forbids first person, forbids inventing an instrument, person, place, object or food, and assigns each paragraph to specific memories so none get dropped:

- Third person only. Never use "I", "me", "my", "we" or "our".
- Every sentence must be supported by the memories above.
- Write exactly 3 short paragraphs, separated by a blank line.
  Paragraph 1: only THE KITCHEN and THE SMALL THING.
  Paragraph 2: only THE SONG and THE PLACE.
  Paragraph 3: only THE SAYING and THE UNFINISHED THING.
- Do not repeat a phrase, an idea or an image from one paragraph in another.

Answering is strict, and it refuses before the model is ever called:

// The archive decides what is true. The model only says it gently.
const hit = retrieveFromArchive(question, ofrenda)
if (!hit) return { answer: 'The flame has no memory of that.', grounded: false }

// If something matched, the model may only rephrase it —
// and the app attaches the citation. The model does not choose its sources.

Ask "What was her mother's name?" and you get "The flame has no memory of that." No hallucination, no polite invention, no guess dressed up as memory. That single behavior changed how I designed everything else, because it let me be honest in the interface: the six memories are always shown next to the remembrance, and the reading says out loud that the archive is the source of truth.

Two more decisions I'd keep:

  • No microphone, on purpose. Browser speech APIs often send audio to a vendor. A local-first grief tool that quietly uploads your voice is not local-first, so I shipped typing only.
  • Candle mode is not a fallback. It is the guarantee. If the model disappears, changes, or refuses to load, the room still works and your words stay exactly as you wrote them.

And then there was the honest bug. I started with the newer, smaller Gemma 3 1B. Its prebuilt WebLLM record cannot load — it throws Only one of context_window_size and sliding_window_size can be positive — and the suggested config fix loads the model but produces garbage, because Gemma 3 interleaves global layers with 512-token sliding-window layers (mlc-ai/web-llm#873). So I ship Gemma 2 2B, which runs correctly, and left the model id as a single line in src/lib/ai/webllm.ts for the day that build is fixed. That is what "you can swap models" actually looks like in practice.

Verified, not claimed. On a desktop Chrome with WebGPU: the model downloaded 1.4 GB in about two minutes, wrote a three-paragraph remembrance from six memories in 13.6 seconds on-device, and answered a grounded question in 1.9 seconds with a citation attached. The app is a PWA, so after the first run the shell and the model are cached and it works offline. The keepsake export is a 321 KB HTML file with zero

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies Find out more here