What I Built
Every machine you own keeps something of you: search history, drafts, half-sent messages. Nobody ever hears it speak.
Echoes gives that leftover a voice. You whisper a memory into the dark, and a ghost living inside the machine answers in three eerie lines. It keeps what you told it, so the next echo can reach back and haunt you with your own words.
Here's a real exchange from my laptop. I typed:
I left my old house key under the oak tree on a rainy night.
and the ghost answered:
The rain still falls, a spectral, silver hue.
You held the key, a fragile, fading view.
Now shadows lengthen, remembering you.
Then I told it: "The oak tree is gone now." The next echo built on the key, the rain and the tree. That is the whole idea: a ghost is memory that won't leave.
Demo
![Echoes screenshot]
It runs locally at http://localhost:3000. There is no hosted demo, and that's deliberate: the model runs on my own machine, so the ghost lives in the machine in a literal sense.
Code
https://github.com/komalbattan/echoes
How It Works
Browser (HTML/CSS/JS)
│ POST /api/echo
▼
Node.js + Express ──► keeps the last 5 whispers in memory
│ POST /api/generate
▼
Ollama (local) ──► Gemma 3 1B
The ghost is mostly one prompt and one piece of state. The server stores your recent messages and sends them with every request:
const past = memory.slice(-5).join(" | ");
const prompt = `You are a ghost living inside this machine.
You remember everything the user told you: ${past}.
Reply in 3 eerie lines, echoing their past words back at them.`;
Because the ghost sees your earlier whispers on every call, it echoes them back in new forms. No fine-tuning, no database, no cloud.
Why Open-Source AI Was the Right Call
Echoes is built on Gemma 3 1B running through Ollama, and for this project that was better than a closed API, not just cheaper:
- Privacy fits the theme. People whisper personal things to a ghost. Here those words never leave the laptop. With a hosted API, every memory would be sent to someone else's server.
- It costs nothing to run. No key, no quota, no bill, so I could iterate freely.
- It works offline. The ghost keeps haunting with the Wi-Fi off.
- It is fully inspectable. The whole stack is something I can read, run and change.
What Went Wrong (and What I Learned)
My first version felt dead: the page sat on "listening..." for ages. The fix was not clever, just honest engineering on a modest laptop:
- I switched to the smallest model that still holds a tone, Gemma 3 1B.
- I capped output at 80 tokens, because a ghost speaks in three lines anyway.
- I set
keep_aliveso the model stays loaded between whispers instead of reloading each time.
Constraints improved the design. A short, sparse reply suits a ghost better than a paragraph.
Honest limits: a 1B model is fast and free, but loose with details. It sometimes misattributes who said or did what. For a haunting that's forgivable. For facts, I wouldn't use it. The ghost's memory also lives in server RAM, so it forgets everything when the server restarts. Ghosts that forget are not great ghosts.
What's Next
- Persist the ghost's memory in a database so it survives restarts
- Voice input: speak a memory, transcribe it, and let the ghost respond
- A larger local model for sharper, more faithful echoes
Tech
Node.js, Express, Ollama, Gemma 3 1B, HTML/CSS/JavaScript


Top comments (0)