跳到正文
DEV Community· Kanak Waradkar·· 3 小時前AI 評分38

我為 31 個已消失網站舉行降靈會:最誠實的幽靈説「那從未寫進我的 HTML」

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

AI 導讀

作者為 31 個已消失網站打造降靈會互動體驗,讓幽靈引用網站的真實文字,而非編造歷史。Gemma 4 為 32 個網站條目撰寫內容,項目亦移除了失效時改用閉源模型的後備設定。體驗包含 3D 網站檔案櫃、搜尋、降靈會對話及查證網站結局的功能。

正文

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.

來源:DEV Community · dev.to