DEV Community

Alit Indrawan
Alit Indrawan

Posted on

Horrorscope 👻: I Trapped a 19th-Century Ghost Inside a Web Server

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

What I Built

Horrorscope is a single-page web app that summons a sarcastic, spooky "digital curse" tailored to your zodiac sign. I took the "Ghost in the Machine" theme about as literally as you can: the AI behind it is an ancient, sarcastic 19th-century ghost trapped inside a web server, channeling horoscopes that mix supernatural dread with everyday tech pain — memory leaks, phantom notifications, haunting backlogs, ghostly posture.

You enter your name, age, and zodiac sign, pick an output language (English or Bahasa Indonesia), and hit "Summon My Curse." The ghost replies with two short paragraphs that name a specific "digital curse" or "haunting spirit" currently attached to you. You can copy the curse to your clipboard or have it spoken aloud in a deliberately creepy cadence.

Demo

Horrorscope is a standard Next.js app, so it deploys cleanly to Vercel — add the GEMINI_API_KEY environment variable and it runs. A short screen recording or a pair of screenshots (the summoning form, and the terminal-style result box with a generated curse) goes a long way here, since the aesthetic is half the fun.

Code

How I Built It

Stack: Next.js 14 (App Router), TypeScript, Tailwind CSS, and lucide-react for icons. The model is Google's Gemini Flash, reached through the Google AI Studio REST API — deliberately with a native fetch instead of an SDK, so there's nothing extra to install and the request payload stays transparent.

The "ghost in the machine," literally. The system instruction casts the model as a 19th-century ghost haunting the server, and that voice carries through the whole app. The loading state reads "Channeling Spirits…", every error is in-character ("The spirit realm is currently disconnected…"), and the result lands in a terminal-style window titled CURSE_TRANSMISSION.txt. The theme isn't decoration — it's the narrator.

Resilient model calls. Google rotates and retires model versions, so the route doesn't hard-fail when one disappears. If the configured model comes back unavailable, it queries the ListModels endpoint, filters for text-generation Flash models, and sweeps through candidates until one answers. Transient spikes (429/503) get retried with backoff before that fallback kicks in. The ghost keeps talking even when the model catalog shifts underneath it.

Bilingual curses + a voice. A language toggle rewrites rule 4 of the system instruction so the same ghost speaks English or Bahasa Indonesia. The "Listen" button pipes the curse through the Web Speech API at a slower rate and lower pitch for a deliberately unsettling read-out, with best-effort voice matching for the chosen language.

The aesthetic. A custom Tailwind theme drives the mood: an ectoplasm green / banshee purple / blood red palette, glowing box-shadows, and flicker + fade-in-up keyframes. The body sits on a slate-950 base with three drifting radial "fog" gradients so it never feels flat-black. Zodiac signs are picked from a grid of their actual astrological glyphs (♈♉♊…) rather than a dropdown, which makes the form feel like a séance board.

Error handling. The route validates inputs (400), checks for a missing GEMINI_API_KEY (500), and wraps upstream failures (502) — each with a spooky message instead of a raw stack trace, so even the failure states stay on-theme.

The Spirit of It

I wanted the "ghost in the machine" to be more than a theme — I wanted it to be the app's narrator. The ghost isn't a metaphor here; it's the thing answering you. The moment it landed for me was the first time an error path printed "The spirit realm is currently disconnected…" instead of a generic 500, and it actually felt right. The loading spinner says "Channeling Spirits…", the curse arrives in a terminal window labeled CURSE_TRANSMISSION.txt, and you can make the ghost read its own curse back to you in a low, slow whisper.

It's a small app, but the idea — that the machine itself is haunted, and the haunting is the feature — is what I wanted people to feel in the first few seconds.

Top comments (0)