DEV Community

Cover image for I held a séance for 31 dead websites. The most honest ghost said "that never made it into my HTML."
Kanak Waradkar
Kanak Waradkar Subscriber

Posted on Edited on AI-assisted

I held a séance for 31 dead websites. The most honest ghost said "that never made it into my HTML."

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

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

In 1996 a 13-year-old drummer from St. Louis built a GeoCities page called The Vortex. Its tagline was "A Strange Place for Cool Stuff." Near the bottom of the HTML, where nobody was meant to look, he left a comment:

<!-- This is where it ends------- -->
Enter fullscreen mode Exit fullscreen mode

The Wayback Machine last captured that page in 2001. This week I took it down off a shelf and asked it who it was:

My name is Dustin Miles, and I am a 13 yr old attending St.Vincents De Paul school. I am a practicing drummer from St.Louis Missouri who likes sports, the internet, The X-Files, and paranormal related things. I created this as "A Strange Place for Cool Stuff" and I hope ya enjoy my "strange corner of the web."

Every fact in that answer is in the page he wrote thirty years ago. Then I asked what he'd hidden in the source:

The Vortex pulled off the shelf: its cover is a 1996 browser window drawn from the real page text, and its ghost quotes every HTML comment it left behind, highlighted in marigold
The Vortex's case cover is drawn from its real 1996 page text. Every marigold quote in the reply is a real comment from its source.

The model that said it is Gemma 4, an open-weight model, and it is only allowed to remember what survived in the HTML.

TL;DR

  • What: Closet 404, a candlelit walk-in closet of 31 dead websites (Kozmo, theglobe.com, Pseudo, Friendster, AltaVista, Vine, GeoCities Area51…). Pull a case off the shelf, read its remains, and hold a séance with it.
  • The rule: the ghost may only say what's in its archived HTML, plus whatever the coroner found. Quotes are highlighted so you can check them. Asked why it went quiet, Kozmo's ghost said: "the records say I went defunct in 2001 because of "Liquidation" [1]… The rest of my story never made it into my HTML."
  • How it ended comes from somewhere else: a coroner runs a live web search (SerpApi) for each domain, and Gemma summarises only those results, with [n] citations. 30 of 31 sites have a report, from 26 searches.
  • Real remains: 113 HTML comments and 170 image references recovered from Wayback snapshots with the id_ flag. Nothing in data/ is hand-written.
  • The bug that mattered: my first pipeline quietly used closed models. 32 of 32 eulogies were written by one. Now 0 of 31 are: every one is Gemma 4, regenerated from the stored HTML.
  • Cost: $0. One env var moves it between local Ollama and hosted Gemma 4 weights.
  • Walk in: closet-404.onrender.com · code on GitHub

What I Built

Think of the Criterion Closet, if the films were dead websites and the closet had been dressed for Día de los Muertos: papel picado strung over the top shelf, candles flickering, heaps of cempasúchil between them.

The closet: papel picado over an ofrenda of candles and cempasúchil, above three shelves of dead websites with a brass label reading DOT-COMS · PORTALS · SEARCH ENGINES
The back wall. Colored spines are real dead sites; the dark ones around them are blanks.

Every case on those shelves is a real site that went dark. Here's what you can do in there:

  • Pull a case. Click a spine and the case flies to you and turns to show its cover: a little period browser window drawn from the site's real scraped text and its first HTML comment. Drag to spin it. The eulogy is on the back, labeled with the Gemma model that wrote it.
  • Hold a séance. Talk to the site. Gemma plays its ghost from the snapshot's comments, image filenames, generator tag and page text. Anything it quotes shows up in marigold, so you can check it against the Remains tab.
  • Read the coroner's report. A 2000 snapshot of Kozmo has no idea Kozmo would shut down in 2001. The coroner tab does, and cites where it read it.
  • Inspect the remains. The raw forensics: comments, images, outbound links, generator, page text, capture years, and what the address returns today. Nothing on that tab was written by a model.
  • Shine the flashlight. Search by memory, like "UFO", "free music" or "dating". Matching spines glow marigold and slide out.
  • Resurrect a URL. Paste any dead domain. The server pulls it from the Wayback Machine, Gemma writes its eulogy, and a new case appears on the "Resurrected tonight" wall.
  • Leave a marigold. A remembrance note on the site's ofrenda.

Searching "free music": mp3.com, Audiogalaxy, Kazaa and theglobe.com glow marigold and slide out of the shelf
"free music" lights up mp3.com, Audiogalaxy, Kazaa and friends. Search is plain keyword matching, so Kozmo answers to "FREE delivery" too.

Clicking the Kozmo.com spine: the case slides off the shelf, flies to the camera, turns to show its cover, and spins
Pull a case and it turns to show its cover. Drag to spin it; the back carries the eulogy.

How I read the theme

Día de los Muertos isn't about ghosts that frighten you. It's about the dead coming home for a night to the things they loved, while the living set out what they remember. The early web is full of people like Dustin, who built something with a text editor and a free host, and then the host switched off. I wanted a place where those pages come home for a night and speak in their own words, and where you can see exactly which words were theirs.

Demo

👉 closet-404.onrender.com: sound on, drag to look around, scroll to lean in. It's on Render's free tier, so if it has been idle the first load takes about a minute to wake up.

A few things to try:

  • Search UFO, then drag to look at the left wall (the GeoCities Area51 neighbourhood), pull The Vortex and press "Your HTML comments."
  • Pull Kozmo.com and ask "Why did you go quiet?" Then open the Coroner tab.
  • Pull Pseudo.com and open Remains: its homepage music was commented out, <bgsound src="homepage/chimes.wav">.
  • Press U and resurrect a domain that isn't on the shelf, like suck.com.
  • Read the index if 3D isn't your thing. Every site also has a plain dossier page, for example The Vortex.

Resurrecting suck.com live: the URL is typed in, and after the wait a new case appears and opens with a Gemma eulogy and a resurrected-live tag
Resurrecting suck.com live, sped up. The new case lands on the "Resurrected tonight" wall.

Kozmo.com pulled off the shelf, its cover showing the real 2000 homepage text, and its ghost answering "Why did you go quiet?" by citing the coroner's record
Kozmo's ghost quotes its own homepage ("Minimart", "Bestsellers") and cites the coroner for how it ended.

The Coroner tab: Kozmo.com was founded in 1998, became defunct in 2001 following liquidation, with a numbered citation to Wikipedia
The coroner's finding, with the search result it's allowed to use.

A séance with Kozmo.com, sped up: the question, then the ghost's reply streaming in with marigold quotes and a numbered citation
A séance, sped up and with Gemma's thinking time cut.

Gemma 4 thinks before it speaks. In my tests today a reply took anywhere from 3.5 to 47 seconds to start, depending on how long it thought. While it thinks, the drawer shows a live "Gemma is thinking" counter.

If 3D isn't your thing, every site has a plain dossier page and there's a sortable index:

The Vortex's dossier page: eulogy, séance, coroner and remains on one page

Code

GitHub logo Labreo / closet-404

An ofrenda for the dead web: a candlelit 3D closet of dead websites, read aloud by an open-weight Gemma ghost.

Closet 404

An ofrenda for the dead web. A candlelit walk-in closet of websites that went dark, pulled back out of the Wayback Machine. Take a case off the shelf and hold a séance: an open-weight Gemma 4 model speaks as the site, remembering only what survived in its archived HTML.

Built for the Hacktoberfest 2026 Open-Source AI Challenge, week 4: Ghost in the Machine.

stack stack stack


What you can do




















Walk the closet Drag to look, scroll to lean in, W/S to crouch. 31 real sites sit on the shelves among unlabeled blanks, under papel picado, candles and cempasúchil.
Pull a case Click a spine. The case flies to you and turns to show its cover: a period browser window drawn from the site's real scraped text and its first HTML comment. Drag to spin it; the back cover carries the eulogy.
Hold a séance Chat with the site's ghost.
…







git clone https://github.com/Labreo/closet-404 && cd closet-404
npm ci && cp .env.example .env.local   # defaults to local Ollama, no keys
ollama pull gemma4:e4b
npm run dev
Enter fullscreen mode Exit fullscreen mode

How I Built It

data/seed-urls.txt
  └─ build-archive.ts  Wayback CDX ─▶ id_ raw HTML ─▶ charset decode ─▶ cheerio forensics ─▶ Gemma 4 (JSON)
       ▼
data/archive.json + data/raw/*.html          (committed, never hand-edited)
  └─ coroner.ts        SerpApi web search per domain ─▶ Gemma 4 summary, may only cite [n]
       ▼
data/coroner.json
  ├─ /            3D closet (React Three Fiber)   ─ /api/seance     streaming Gemma ghost
  ├─ /archive     editorial index                 ─ /api/search     BM25 over text + Gemma vibe tags
  └─ /site/[id]   static dossier per site         ─ /api/resurrect  live Wayback + Gemma
                                                   ─ /api/coroner    live SerpApi inquest
Enter fullscreen mode Exit fullscreen mode

The remains are real

scripts/build-archive.ts does the digging, slowly on purpose:

  1. Ask the Wayback Machine's CDX index for every 200-status HTML capture. The first and last rows give the capture years.
  2. Pick an early capture with real content and fetch it with the id_ replay flag, which returns the original bytes without the archive toolbar.
  3. Decode it in its real charset (lots of 1990s pages are windows-1252), then pull out comments, images, links, generator and visible text with cheerio. Framesets and image-only pages are skipped, never guessed at.
  4. Knock on the live address to see what it returns today. 15 of the 31 are unreachable or now redirect somewhere else.
  5. Hand only those forensics to Gemma for a title, a eulogy, a ghost persona and eight vibe tags.

It sleeps 4 seconds between archive.org requests and backs off on 429s. The output is committed: data/archive.json, plus the raw HTML of every snapshot in data/raw/.

Pseudo.com's Remains tab, showing its HTML comments including a commented-out bgsound tag for homepage/chimes.wav, and its image filenames
The Remains tab is raw scrape, no model involved. Pseudo's homepage music is still sitting in a comment.

A few things it dug up that I'd never have found by hand:

Site What was in the source
Pseudo.com (1997) its background music, commented out: <bgsound src="homepage/chimes.wav">
Patrick's Home Page (GeoCities) the host's own injected markers: text above generated by server. PLEASE REMOVE
The Vortex (GeoCities) <!-- Created by GeoCities Home Page Generator --> and <!-- This is where it ends------- -->
theglobe.com ad buttons left in comments, pointing at goto.com/images-promoters/theglobe/

Keeping the ghost honest

The séance prompt (src/lib/seance.ts) gives Gemma two blocks: SOURCE, the snapshot, and RECORDS, the coroner's numbered search results. The rules: stay in character, state nothing outside those two blocks, copy quotes exactly, and if something isn't recorded, say so in character.

Before that rule, I asked the same model about Kozmo with no source attached. It confidently explained that its "burn rate outpaced profitability." That's plausible history, but nothing I could point to. With the rule in place, and before the coroner had run, Kozmo's ghost said:

I was moving so fast, just rushing to get "Snacks, sandwiches, drinks, ice cream and other tasty treats" to every doorstep in under an hour! I don't know why I went quiet, because that never made it into my HTML.

That's the right answer. A ghost that makes things up is just a chatbot in a costume.

So the cause of death comes from the coroner. For each domain it runs one Google search through SerpApi ("kozmo.com" shut down history) and keeps results with real links. Gemma then writes a two- or three-sentence finding that may only cite those numbered results, and the UI turns [1] into a link. Some findings it produced:

  • Pseudo.com: "shut down on September 20, 2000 [1]", citing ABC News, Pseudo.com Shuts Down.
  • HotBot: "In October 2016, Lycos sold the HotBot.com domain name to an unnamed buyer for $155,000 [1]."
  • The Vortex: "hosted on Geocities, which Yahoo permanently shut down [2]", citing TechSpot, Oct 26, 2009.
  • Xoom: "Cause of death unrecorded." The search didn't say, so neither does the coroner.

The closet

  • React Three Fiber on Next.js 16. The 31 real cases are individual meshes. The ~400 unlabeled cases around them are one InstancedMesh, with a gap here and there and a neighbour leaning into it.
  • Textures drawn once per site on canvases, after the fonts load. The spine is one of three palette treatments derived from the site's accent color, the cover is a period browser window with the real page text, and the back cover holds the AI-labeled eulogy. Mipmaps and full anisotropy mean you can lean in and read them.
  • The ofrenda is procedural. The papel picado is a canvas with a calavera cut out using destination-out, fluttering on a string. Candle flames are HDR spheres picked up by bloom, and their lights flicker toward damped random targets. The cempasúchil are a few hundred instanced icosahedra heaped into mounds.
  • Curated walls with brass plaques: dot-coms on the back wall, the GeoCities Area51 neighbourhood on the left, and this visit's resurrections on the right.

The left wall: a run of GeoCities Area51 pages (Patrick's Home Page, The Vortex, The Alien & UFO Home Page, life goes on., MJ-12) under a brass plaque reading GEOCITIES · AREA51 NEIGHBORHOOD
The Area51 neighbourhood. Hovering a spine shows its title and capture years.

  • Sound with zero audio files: Web Audio noise bursts for spine ticks and case slides, a low room tone, and candle crackle.
  • All motion uses maath/easing damping, and nothing touches React state inside useFrame.

Bugs and gotchas I hit

  • The closed model hiding in my pipeline. More on that below.
  • Hosted Gemma 4 always thinks. On Google AI Studio's OpenAI-compatible endpoint, reasoning_effort is rejected with "Thinking budget is not supported for this model." One séance reply spent ~1,460 tokens thinking for an 80-token answer (34 s). So the client strips the <thought> blocks from the stream, and the séance stream sends a one-byte heartbeat for every thinking chunk to drive the trance meter.
  • Thinking can eat the whole answer. On a long prompt, Gemma sometimes spent the entire token budget thinking and returned nothing. I raised the hosted budget, but one coroner report ("life goes on.", GeoCities) still failed three times. Its tab offers a live inquest instead of a made-up finding.
  • gemma-4-31b-it returned HTTP 500s all day while gemma-4-26b-a4b-it answered fine. The client now falls back to a second Gemma model, never anything else.
  • My monospace font ate the evidence. JetBrains Mono's ligatures rendered <!-- as <!—, so the HTML comments the whole project is about looked wrong. The fix was font-variant-ligatures: none on every code surface.
  • SerpApi timed out on a few searches. The coroner script retries, caches searches per domain, and resumes where it left off.
  • Empty replies. While capturing screenshots for this post, a séance came back blank after almost two minutes: Gemma had thought until it ran out of tokens. The ghost now gets a "think briefly" rule, and the chat retries once on its own before showing "Only static came back."
  • My first Render deploy failed with Cannot find module '@tailwindcss/turbopack'. I had set NODE_ENV=production on the service, which made npm ci skip dev dependencies. Next.js sets production mode on its own, so the fix was to delete the variable.

Why Does Open Innovation Matter?

The web in this closet didn't die for lack of storage. It died because somebody switched it off: a host shut down, a startup ran out of money, a platform changed its mind. An archive of switched-off things shouldn't depend on a service that can be switched off.

I learned how easy it is to get this wrong. My first pipeline was generated with an AI coding agent, and it took a "helpful" shortcut: it pinned a closed hosted model at startup and fell back to another one whenever a call failed. When I audited the data, all 32 eulogies had been written by closed models, and nothing in the UI said so. I ripped the fallback out, made the only fallback a second Gemma, and regenerated every eulogy from the stored HTML with npm run data:ghosts. Each record's generatedBy now says which Gemma wrote it.

What open weights made possible:

  • One env var moves the model, not the app. LLM_BASE_URL points at Ollama running gemma4:e4b locally, or at Google AI Studio serving the same Gemma 4 weights for the hosted demo (Render has no GPU). It's the same fetch client and the same prompts, with no SDK.
  • It runs with the lights off. Clone it, ollama pull gemma4:e4b, npm run dev. The whole archive is in the repo, so séances work offline.
  • Anyone can check my ghosts. npm run data:ghosts -- --force regenerates every eulogy from the stored HTML. You can diff your results against mine.
  • It costs nothing. Regenerating all eulogies and every coroner finding ran inside the AI Studio free tier, and the local path is free.

Where it was worse, honestly:

  • My laptop is too small for local. gemma4:e4b is 5.5 GB on disk. On my 8 GB M3 it ran 71% on CPU and swapped. The local path works, but you want 16 GB.
  • The hosted demo is not private. On the AI Studio free tier, Google may use prompts to improve its services. Only public archived pages are ever sent. For a private séance, run it locally.
  • A small model paraphrases. The prompt demands verbatim quotes and the UI highlights them, but now and then a quote is reworded. The Remains tab is right there for checking.
  • Search is keyword (BM25) over the page text and Gemma-written tags, not embeddings.

My Agent Session

Closet 404: finding a closed-model leak and rebuilding a Gemma séance for dead websites
You

Conversation with Gemini
"
Skip to content
Powered by Algolia
Create Post
Challenges
→
Hacktoberfest Open-Source AI Challenge: Week 4
Hacktoberfest Open-Source AI Challenge: Week 4
Signed Up
View Entries
Challenge 5 of 5 in our Hacktoberfest DEV Challenges. One prompt, a new theme every week.
Challenge Status:
Live
This year's Hacktoberfest is not about open source pull requests. There's no PR count to hit and no repos to hunt for. Instead, you build a brand-new project with open-source AI at its core, and write about it on DEV. Here's everything you need to know about Hacktoberfest 2026.
Week 4 of our five Hacktoberfest DEV Challenges starts today, and it's our final challenge of the series! Running through October 31, the Hacktoberfest Open-Source AI Challenge: Week 4 gives you the week to build, wrapping up on Halloween night just as Día de los Muertos begins.
Missed Week 3: Slow and Steady? No problem. Every challenge is a fresh start, so you're competing on the same footing as everyone else.
This week, we'll select one overall winner and a winner in each of our 20 partner categories. See all the challenges and current partner categories on the HF26 DEV Challenge Hub.
Free partner credits
Several partners are giving participants credits and promo codes, including Tinker, Render, Backboard, and a free .tech domain. Claim yours at hacktoberfest.com/my/promos.
Claim your credits →Visit hacktoberfest.com
Key Dates

Contest start:  
October 26, 2026  
Submissions due:  
October 31, 2026  
Winners announced:  
Week of November 2

Badge Rewards
Hacktoberfest Challenge Completion Badge
Hacktoberfest Challenge Winner Badge
Find Out More
Ask questions and share your ideas on the
Hacktoberfest Open-Source AI Challenge: Week 4 Launch Post.
View Launch Post
Challenge Prompt
Ghost in the Machine
Build something with open-source AI at its core.
That can mean running an open-weight model, building on an open-source agent harness or framework, running inference locally, or all three. Whatever you pick, the open pieces should be what makes your project work.
In your post, tell us why open innovation matters for what you built. Does it run on a laptop with no internet? Keep someone's data off a server they don't control? Let you fine-tune, swap models, or change how your agent behaves? Cost nothing to run? Tell us where your open-based approach worked better than a closed one.
This Week's Theme: Ghost in the Machine
Build something spooky, or something that honors the past in the spirit of Día de los Muertos, with open-weight models or open-source AI.
This week brings us Halloween on October 31 and Día de los Muertos on November 1 and 2, a stretch of the calendar when the line between past and present feels a little thinner. Whether that brings to mind things that go bump in the night, ofrendas glowing with candlelight and marigolds, or the memories and stories we carry with us, we want to see what it inspires you to build. The theme is intentionally broad, so interpret it however you'd like!
That can mean running an open-weight model, building on an open-source agent harness or framework, running inference locally, or all three. Whatever you pick, the open pieces should be what makes your project work.
A few ideas to get you going:

A haunted terminal that "possesses" your shell and whispers back eerie commentary on the commands you run  
A virtual ofrenda builder that helps you write a remembrance for a loved one and gather their favorite foods, songs, and stories  
An offline séance chatbot that channels the spirit of a long-dead programming language  
A local oral history tool that records and transcribes family stories so they're never lost  
A ghost story generator that weaves your smart home's sensor logs into a creepy tale of what happened while you were asleep  
A project that brings an abandoned open-source tool or a forgotten corner of internet history back to life 

In your post, tell us why open innovation matters for what you built. Does running your own model mean your project works with the lights (and the internet) off? Keep your family's photos and stories private and on your own machine? Make sure what you build isn't locked inside a service that might disappear someday? Tell us where your open-based approach worked better than a closed one.
Submission Template
Show your work. We'd love to see how you built it. Save your agent session with DevRelay and embed it in your post, or link to it. It's optional, but it helps the judges understand your process.
Judging Criteria

Writing Quality (weighted most heavily)  
Relevance to the Prompt and Theme  
Creativity  
Technical Execution  
Use of Partner Technology (optional) 

Top Prize
$250 USD

  • DEV++ Membership
  • Exclusive winner badge

Prize Categories
Featured categories
$200 for each winner
Best Use of Render
Use Render as your project's AI runtime, to host an agent's front end, or to run Hermes or OpenClaw.
Prizes
$200 USD

  • Exclusive winner badge

Best Use of TabPFN
Use TabPFN, Prior Labs' tabular foundation model, to forecast, predict, classify, or spot anomalies from historical data like a CSV. Prior Labs strongly recommends the latest version, TabPFN-3.5: it's open-weight and free to use once you sign the non-commercial license at platform.priorlabs.ai, and you can then run it locally. The API adds features like Thinking Mode. Prior Labs favors projects that use TabPFN inside a wider prediction agent (with or without the MCP server).
Prizes
$200 USD

  • Exclusive winner badge

Best Use of Tinker
Use Thinking Machines' Tinker to fine-tune a model for a specific task, and show a clear improvement in performance, latency, or cost over a baseline.
Prizes
$200 USD

  • Exclusive winner badge

Best Use of Arduino
B
…[74674 chars trimmed]…
that would win the hackathon.Also the partner categories havent been itnegrated so we need to integrate those if you need something pelase let me know it is colloabrative process.

Agent

Found a hidden landmine: pipeline.ts silently swaps to closed gemini-3.5-flash-lite on any failure — that's why 21 of 32 entries say Gemini. Checking build health and Gemma connectivity next.

Hosted 31B is 500ing on Google's side right now; Gemma 4 26B-A4B works. Checking local Ollama speed.

Local Gemma e4b swaps on this 8 GB M3 (71% CPU) — too slow for demo. Will use hosted Gemma 4 for generation, keep local path documented. Testing if hosted thinking can be turned off for speed.

Now rewriting the archivist prompt (grounded, quotes a real detail, no closed-model fallback, no fake tag padding).

Grounded and good — quotes real text, no invented history. But ~2 min per site, so I'll run 4 in parallel in the background while I build the UI.

While that runs: removing the closed-model override from build-archive.ts too.

Params are Promises in Next 16 — noted. Now let me see the current 3D closet in the browser before redesigning.

A dev server is already running on :3000 — attaching to it.

Current state: dim, monochrome, spines unreadable, canvas doesn't fill viewport. Rebuilding the visual layer. Starting with fonts/tokens and the data + séance backend.

Now backend: types, data loader, séance prompt, rate limiter, streaming route.

Séance works — quotes real comments verbatim. Reconstruct mode came back empty; debugging.

Hosted Gemma timed out under load from the 4 regen workers — will retest after. Now the 3D rebuild. Checking exact R3F/maath APIs first (per RULES #4).

31/32 now written by Gemma 4. Retrying the one failure in background while I continue the 3D code.

Now store + room layout + case mesh.

All 32 now Gemma-written. But delicious.com's 1996 snapshot turns out to be an adult link index — real data, but bad for a judged demo. Checking others for the same.

Only delicious.com is a problem. Adding a small prune script to drop it and renumber spines (keeps "only scripts write data/" rule).

Now the case mesh: three textured faces, hover slide, search glow pulse, pull-and-spin with cover facing camera.

Now procedural WebAudio foley (zero asset files).

Now the room architecture, Día de los Muertos décor (candles with flicker, cempasúchil clusters, papel picado), and camera rig.

Now the HUD layer. Checking the existing ResurrectModal interface first.

Now the séance chat component (streaming, "medium is listening" state, voice playback).

Now the drawer with tabs: Séance · Coroner · Remains · Ofrenda.

Now the Coroner: SerpApi web search for how each site actually ended + Gemma summary that must cite sources. Verifying the SerpApi key first.

SerpApi works (244 searches left) and finds real obituaries ("Kozmo Calls it Quits", The Hoya, Apr 2001). Building coroner lib, bake script, and live route.

Coroner bake running in background. Writing CoronerPanel + live inquest route.

Now letting the séance accept a live-resurrected site, then the HUD shell.

Now the HUD shell: intro gate, top bar, command bar with search, hover label, and the app orchestrator.

Status: new 3D closet, HUD, séance, coroner, voice routes all typecheck + lint clean. Now viewing it in the browser.

Fonts aren't resolving — --font-display in @theme references itself (circular). Renaming next/font variables.

Huge jump from before — colored spines, papel picado, candles, marigolds. Testing hover + pull-and-spin.

Hover label works. Click selected the site but the drawer didn't render and the case didn't fly. Checking console.

Those are stale HMR errors from mid-edit. Checking whether the drawer rendered at all.

That's the hero shot — cover is a period browser window rendered from the real scraped Kozmo text. Testing a live séance in the drawer.

Waiting on Gemma. Meanwhile writing the 2D index (/archive) and the per-site dossier page (/site/[id]).

Search glow works (MJ-12, The Vortex light up). Fixing: stuck hover slide after put-back, stopwords in search ("the" matched Go2.com), stronger glow, fuller marigold heaps.

Checking mobile layout.

The dev server on :3000 went down (it was the user's process). Starting my own on :3404.

Mobile works. Small fixes: wider FOV in portrait, hide desktop-only hints and subtitle on phones.

Core experience done. Now partner integrations + docs. Updating env template and Render blueprint for the new services.

Now the required gate: tsc, lint, build.

All three gates pass: tsc clean, eslint clean, next build succeeded (43 static pages incl. 31 dossiers). Checking the coroner bake.

Some SerpApi searches returned 503; adding retry with backoff so the resumable re-run catches them.

While the coroner bakes: capturing a real séance transcript for the DEV post's cold open (no invented quotes).

Empty replies after 55–90 s — likely free-tier contention with the 3 coroner workers. Checking server logs.

Non-streamed: 34 s, ~1,460 thinking tokens then an 80-token answer quoting the page verbatim. Retesting the streamed path to isolate the empty-reply bug.

Streaming works (TTFB 29 s — Gemma 4 thinks before speaking). To make that wait feel alive, I'll stream a heartbeat byte per thinking chunk and show a live "trance" meter in the UI.

Testing the trance meter live in the browser.

Dossier page renders well and the ghost quotes its real comments. JetBrains Mono's ligatures turn <!-- into <!— — disabling ligatures in code-ish text.

SerpApi is slow/503-ing; the bake is still in its search phase. Drafting the DEV post meanwhile — pulling real stats first.

Fact-checking the post's claims against the data.

Post draft done and fact-checked. Checking coroner bake.

SerpApi itself is fast now (1.5 s); the bake was stuck on long-hanging requests with no timeout and a heavy OR query. Restarting it with a simpler query, 30 s timeout, and retries.

Coroner re-bake running. Meanwhile: fixing stale "12s" copy in the resurrect modal and adding a proper favicon.

Adding a social share card (Open Graph image) so the DEV post and links preview well.

Status: fixing two small issues — OG image layout error and a Next 16 dev warning on /site/[id] params. Coroner bake still running.

Usable; the raw accent colors are garish. Switching the OG spines to the same muted palette as the 3D spines.

Good. Checking coroner bake progress.

Searches progressing (SerpApi is slow today). Meanwhile testing live "Resurrect a URL" end to end with a site not on the shelf (suck.com).

Resurrection works live (Suck, 1996–2017, eulogy by Gemma). Quick visual check of the side walls and the hover fix.

Status: rearranged closet (GeoCities wall + brass plaques); re-checking in browser (tab id changed).

You're exploring the closet yourself in the pane right now (pulled "life goes on." and started a séance) — I'll stop driving the browser so I don't interfere. Running the full gate meanwhile.

Build green. Coroner bake finished — checking results.

25/31 coroner reports with real citations (Kozmo → Wikipedia; Pseudo → ABC News "Pseudo.com Shuts Down"; GeoCities → TechSpot Oct 2009). Re-running for the 6 that failed (resumable).

Fixing the metadataBase warning so share cards resolve to the real domain on Render.

Running the project's own audit checks (RULES.md: no TODO/mock/any/ts-ignore/eslint-disable, no closed-model references in data).

Audit clean: no banned patterns, every record now gemma-4-26b-a4b-it. Checking GitHub CLI for the deploy step.

While the coroner retry runs, a quick check of what DEV writers say about this stack (DevRelay).

The app now works end to end on your machine. You were already using it in the browser pane, at http://localhost:3404.

What I found
- Every eulogy came from a closed model. The old pipeline hard-coded gemini-3.5-flash-lite and quietly switched to it whenever a call failed, so all 32 eulogies were written by Gemini. That breaks the challenge's open-source rule. I removed the switch, made the only fallback another Gemma model, and rewrote every eulogy with Gemma 4.
- The home page was still the Next.js starter page. The séance API didn't exist, and fonts and search highlighting weren't hooked up.
- I dropped delicious.com. Its 1996 snapshot is an adult link index, which you don't want in front of judges. I removed it with a new scripts/prune.ts, so 31 sites remain.

What's built
- The 3D closet:
- Colored spines you can read up close.
- Pull a case and its cover shows an old-style browser window drawn from the site's real archived text. The eulogy is on the back, labeled as written by Gemma.
- Día de los Muertos decorations: papel picado, flickering candles, marigold piles.
- GeoCities pages have their own wall, with brass labels on the shelves.
- Sound effects made in code, so there are no audio files.
- Séance: answers stream in and quote the archived page word for word, with quotes highlighted. When the page doesn't say something, the ghost admits it. Gemma 4 takes 10–30 seconds before it starts answering, so I added a live "Gemma is thinking" counter to fill the wait.
- Coroner (your SerpApi entry): it searches the web for how each site ended, and Gemma summarizes only those results, with numbered citations. 25 of 31 sites have a report so far; a retry for the other 6 is running in the background.
- Other pages and features: the index at /archive, a page per site at /site/[id], glowing search results, live "Resurrect a URL" (tested on suck.com), memorial notes, ElevenLabs voice with a built-in browser voice as backup, and a share image for links.
- Docs: I rewrote README.md and drafted the DEV post in DEV_POST.md. I checked every quote and number in the post against the data.

Partner prizes
- Gemma: done, it's at the core.
- SerpApi: done (the coroner).
- ElevenLabs: the code is ready and needs your ELEVENLABS_API_KEY.
- Render and .tech domain: render.yaml is ready. This needs a GitHub repo, a deploy, and you claiming the domain at hacktoberfest.com/my/promos.
- Entire: save this agent session for the write-up.
- Skipped: TabPFN and Sentry. TabPFN would be forced with only 31 rows, and Sentry needs a new package.

Things to know
- Local gemma4:e4b is too slow on your 8 GB Mac, so generation uses the same Gemma weights hosted on Google AI Studio. The 31B model returned errors today; the 26B one works.
- The post says the regeneration cost $0 on the free tier. Confirm your AI Studio key has no billing enabled before you publish that.
- Arrow keys and A/D didn't seem to turn the camera in my test, but dragging works. I haven't fixed this.

Still yours to do
- Record the 60-second video.
- Replace the LIVE_URL, VIDEO and REPO_URL placeholders in the post.
- Publish before 11:59 PM PDT tonight, which is 12:29 PM IST tomorrow.

A few DEV write-ups on this kind of 3D work, if you want reference points:
- 🌱 Testing 1,000 instanced plants in WebGL vs WebGPU — Three.js WebGPU vs WebGL (Alicia)
- 📱 Keeping a heavy WebGL site fast on a phone and readable by search engines — A 3D portfolio Google can read and a phone can run (jonasjavier)

The coroner retries are done: 30 of the 31 sites now have a cause-of-death report, and the production build passes again.

New reports include:
- HotBot: domain sold for $155,000 in 2016.
- GeoCities: shut down by Yahoo, with TechSpot cited.
- GoTo.com: a $21.5M ruling against Disney.

Xoom's search found nothing on how it ended, so its report honestly says "Cause of death unrecorded."

The one site without a report is "life goes on." (GeoCities Area51/1199). Gemma used up its whole token budget thinking on all three tries and never wrote an answer. Its Coroner tab shows a "Hold an inquest" button that runs the search live instead.

The dev server you're using in the pane loaded the coroner data before these reports existed, so Coroner tabs there still show "Hold an inquest." A production build or a restarted dev server will show the reports.

You

unless i already have a code for elevenlabs if you do just send to me all the current codes have been redeemed so we wont be applyign for eleven labs.WE CAN STILL qualify for rnder and .tech domains.tell me what to claim the .tech domain under what name.Also use dev relay to see the winning submisssion of previous hacktobefest dev.to challange winneers and see how theu made thier post and similarily make for ours and make that in to the dev_post md remmeber every weke has a unique submission template so we have to make it according to that. https://dev.to/challenges/hacktoberfest-week4-2026-10-26 Also you should have access to the github repo and you can already deploy it on render.

Agent

Got the exact Week 4 template. Reading the winner announcements to find the overall winners' posts.

Winning titles share a pattern: concrete, first-person, a number or a twist. Reading the overall winners plus the Render, Gemma, SerpApi and .tech winners to see the post structure.

I have the template and the winners' patterns. Now shipping: commit, create the public GitHub repo, push.

GitHub push was blocked by the permission guard because it creates a public repo. I'll ask you to approve it after drafting the post. Meanwhile, checking which .tech names are free.

All candidates are free. I'll recommend closet404.tech. Now rewriting DEV_POST.md to match the exact Week 4 template and the winners' style.

Fact-checking a few lines in the new draft.

Post rewritten to the Week 4 template. Pushing needs your explicit OK because the permission guard blocks creating a public repo on its own.

Pushed to https://github.com/Labreo/closet-404. Switching the blueprint to the Free plan, then deploying via Render.

One workspace (yours). Creating the service with only non-secret env vars; I won't copy your API keys around, so you'll paste 2 keys in the Render dashboard.

No existing closet-404 service. Creating it on Free.

You

Lets skip the .tech domain thing.I alrwady feels 3 sponsers is alot.More would be overkill at this point.

Agent

Got it — dropping .tech. Sponsors stay at three: Gemma, SerpApi, Render. Repointing SITE_URL to the Render URL and checking the deploy.

Build running. Meanwhile: stripping .tech and Entire from the post and docs.

Build is compiling on Render. Committing the doc/config cleanup locally (will push once this deploy is live, so it doesn't cancel it).

Build failed on Render. Pulling the error.

You

your dev_post.md are missing some thing.Like we should have images and stuff.Are you sure that other wining subbmissions didnt have images and sutff.Also my agent sessios is not how it supposed to look and you have halunciated it cause my agent sessions is supposed to be embeded thing.PLESSE DONT HALLUNICATE OR CUT CORNERS SINCE THE ARTICLE IS THE WINNIGN THING.If you can take approprite screenshots,video whatever take it and if you cant take the screenshots or wahtever tell me where to take waht.Also tell me what to demo in that youtube video.

Agent

Typecheck and lint clean. Building production locally and serving on :3405 for clean captures (no dev overlay).

Writing a small Chrome DevTools driver (scratchpad, not the repo) and testing that headless Chrome renders the WebGL closet.

That failure is the old server I stopped on purpose. Now the full capture run. Every step drives the real app; séance replies are live Gemma output, nothing staged.

Captured. Frame rate for GIFs is too low (6 frames in 6.5 s at full resolution); I'll fix that after checking the stills.

The GeoCities wall shot came out well. Two fixes needed: the search never submitted (the synthetic Enter key didn't fire the form), and GIFs need the faster screencast API. Updating the driver.

Pseudo remains and the live resurrection worked. Both séances timed out, so I'm checking what the server returned.

Direct call works (41 s) and the ghost now cites its coroner record: "my fate was liquidation [1]". So the browser-side wait check is at fault. Debugging with a live readout.

Clean. Rebuilding, then recapturing the two séance shots with a longer wait (no spin recording; headless drag events were too slow to give a smooth GIF).

That was the previous server instance being replaced — expected. Running the séance captures now.

Loading the DevRelay session tools. First I'll check whether you already saved sessions (for example from the earlier agent).

No Closet 404 session exists yet, so I'll build one from this Claude Code session's real transcript, with secrets and local paths scrubbed. Locating the transcript file.

Leak check flagged items, so I'm not uploading yet. Inspecting each hit.

Background-task notifications are showing up as "user" turns; they aren't you, so I'm dropping them and checking the two remaining @gmail hits.

The @gmail hits are only my own scrub code, not the address itself. The notification filter edit didn't apply, so I'm making it directly.

This is the Claude Code session where Closet 404 got rebuilt, saved with DevRelay. Secrets, my email and local paths are scrubbed, and screenshots are left out. A few places worth jumping to:

  • The audit. Early on, pipeline.ts turns out to pin gemini-3.5-flash-lite and fall back to it on any failure. All 32 stored eulogies were written by closed Gemini models. Everything after that is the fix.
  • "BLOCKED" moments, handled honestly. Hosted gemma-4-31b-it returns 500s, local gemma4:e4b swaps on an 8 GB laptop, and thinking can't be turned off. Each one is measured before a decision gets made.
  • Dropping delicious.com. Its 1996 snapshot turned out to be an adult link index. It came off the shelf through a script (scripts/prune.ts), like every other data change.
  • Capturing this post's screenshots by driving real Chrome over the DevTools protocol, which is how the empty-reply bug got caught.

The Spirit of It

The moment it landed for me was Dustin. I read his page in the Remains tab first: a kid's page of links to The X-Files, R.E.M. and UFOs, a GuestPAD to sign, and a promise to visitors to "Trust no one, not even people that have visited this page." Then I asked the ghost who it was, and it answered in his words, down to "I hope ya enjoy." For thirty years it had sat in a Wayback crawl that maybe nobody had opened. For one night on Día de los Muertos, it got to introduce itself again.

That's what an ofrenda is for. You set out the things the dead loved, you light a candle, and you let them speak.

Prize Categories

  • Best Use of Gemma: Gemma 4 does every piece of writing in Closet 404: the 31 eulogies, ghost personas and vibe tags (from the scraped forensics only), the streaming séance (grounded in SOURCE and RECORDS, with verbatim quotes highlighted), the coroner findings (only from numbered search results), and live resurrections. It runs locally through Ollama (gemma4:e4b) or hosted on AI Studio (gemma-4-26b-a4b-it, with gemma-4-31b-it as the fallback), behind one env var. That includes the always-on thinking gotcha and the trance-meter heartbeat built to live with it.
  • Best Use of SerpApi: the coroner. One Google search per domain (26 searches cover the shelf, since the GeoCities pages share one; 30 of 31 sites have a finding), filtered to results with real links, then summarised by Gemma with [n] citations that link back to the sources. Baked into data/coroner.json by scripts/coroner.ts, with a live /api/coroner for newly resurrected sites.
  • Best Use of Render: the whole app (3D closet, streaming séance API, live Wayback resurrection, coroner and search routes) runs as one Render web service on the free plan, auto-deploying from main (the same config is in render.yaml). Render sets RENDER_EXTERNAL_URL, which the app uses for its share cards. Model calls go to hosted Gemma 4 because the service has no GPU.

Top comments (0)