This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine
I’ve loved Día de los Muertos for 30 years. I had two days to build something for it.
TL;DR: I built El Altar que Habla, an interactive bilingual ofrenda where you choose one of seven traditional offerings and learn what it means, then ask your own question in English or Spanish. Answers come from Google's open-weight Gemma 4, grounded on a curated knowledge base the server controls. When Gemma is unavailable, a labeled curated guide takes over, so the altar still speaks.
What I Built
I was 17 in 1996 when Día de los Muertos found me. I grew up in the United States, and the celebration has been part of me ever since. I am proud of it, and proud of the connection I have to it.
In 2014 I went to Oaxaca for the first time. It was magic. Mexico during Día de los Muertos is magic, always.
So when this week's theme asked for something that honors the past in the spirit of Día de los Muertos, I didn't have to look for an idea. Of course I wanted to spend weeks or months crafting this piece by piece. The challenge timeline gave me about two days. So I made a small offering of my own: a few of the things I have learned over the years, from me to you.
El Altar que Habla means "the altar that speaks." It is an illustrated ofrenda with seven offerings:
Select a + on the altar, or pick from the list, and a story card opens. Each offering has three tabs: Context, History, and Variations. Variations matters most to me. Families in Oaxaca, Michoacán, Puebla, and across the United States don't all build an altar the same way, and the app says so instead of pretending one practice is the rule.
Below the tabs, you can ask your own question. That is where the altar speaks.
Demo
Live: el-altar-que-habla.vercel.app
Try the water. Switch to Spanish and ask "¿Por qué se pone agua en la ofrenda?" When I asked, Gemma answered that water is a gesture of hospitality for souls after a long journey, then added that how it is presented can vary with each family's and community's traditions. That line traces back to the curated context I gave it.
Code
earlgreyhot1701D
/
el-altar-que-habla
El Altar que Habla is a bilingual, interactive ofrenda sharing the meaning of seven Día de Muertos offerings. Explore stories, history, and community variations in English or Spanish, ask Gemma questions grounded in curated context, and discover a living tradition shared with care.
El Altar que Habla · Día de los Muertos Ofrenda
The altar that speaks. An interactive, bilingual (English and Spanish) Día de los Muertos ofrenda. Choose one of seven traditional offerings, read what it means, and ask your own question. Answers come from Google's open-weight Gemma 4, grounded on curated content from Mexican cultural institutions and UNESCO. When the AI is unavailable, a labeled curated guide answers instead.
Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 4: Ghost in the Machine (October 2026).
| Live app | el-altar-que-habla.vercel.app |
| Open-source AI |
Gemma 4 (gemma-4-26b-a4b-it), served through the Gemini API |
| Prize category | Best Use of Gemma |
| Built by | L. Cordero · Clew Suite |
| License | MIT |
Why this exists
I have loved Día de los Muertos since 1996, and I wanted to share a small part of what I have learned with the DEV community. This is a beginning, not a complete…
How I Built It
This build went through more hands than I planned.
The model only gets what the server gives it
The open-source AI here is Gemma 4, Google's open-weight model family. I used gemma-4-26b-a4b-it, served through the Gemini API. It answers visitor questions about one offering at a time.
One of my requirements from the first draft of the PRD was primary sources. The cultural explanations draw on Mexican cultural institutions and UNESCO, and the PRD says Gemma may explain that approved, cited content but should not generate new historical or cultural claims.
That requirement shaped the risk I worried about most: a confident answer about a living tradition, with an institution's name next to it, when that institution never said it.
So the browser doesn't send cultural context. It sends three things: which offering, which language, and the question. The server checks the offering against an approved list, the seven offerings I signed off on in the PRD, then builds the prompt from its own curated knowledge file. The question is the only part a visitor controls, and it goes in as a question, not as context.
if (typeof offeringId !== "string" || !Object.hasOwn(APPROVED_OFFERINGS, offeringId)) {
return res.status(400).json({ error: "Invalid or unsupported offering ID" });
}
The sources get labeled for what they are. The app links to INPI, INAH, UNESCO's listing for Día de Muertos, and the State of México. Every Gemma answer says those links are background reading. They don't verify the sentence the model just wrote, and I didn't want the page to imply they do. @sidswirl's post on source-grounded hallucination checks put words to that worry better than I could.
One more disclosure lives on the page: the altar artwork is AI-generated concept art, not a photograph of anyone's family altar.
The altar still speaks when the AI doesn't
I kept coming back to one rule: even if Gemma falls back, the experience has to hold.
If the model times out or is down, a curated guide answers instead, labeled "Curated guide (no AI)" in its own readable card. Gemma answers get their own label too. That way a visitor can tell which voice they are hearing. Kiro tested the fallback by forcing the model to fail locally, in both languages.
The painted plus signs lied
The first version was one 469 KB HTML file, with the artwork, styles, and both languages all inside it. That breaks one of my own rules: one file, one responsibility. I built it that way on purpose, as a reference point for the look and feel I wanted to protect. It stays in the repo as a baseline, and Google Antigravity, an agent-first coding tool, split it into separate modules from there.
It looked beautiful. Then I asked Codex for a deliberately harsh QA pass. It scored the app about 5 out of 10 and marked production readiness as failing.
That stung. It was also useful, because now I had a list instead of a feeling.
My favorite finding: the altar artwork had plus signs painted into the image itself. The clickable hotspots were separate elements on top. So you'd see a + that did nothing, sitting right next to a + that worked.
The artwork got cleaned. The hotspots got tied to the image's own coordinates. Then Playwright, the browser testing tool, started checking that every hotspot's center lands inside its painted object on mobile, tablet, and desktop, that each one is at least 44 by 44 pixels, and that none overlap. A test that only checks the page's code could pass the broken version.
"Fixed" wasn't the finish line
Antigravity kept telling me fixes were done. I'd open the app and see the same problem. I don't know what happened inside the tool. I know the assurance and the screen didn't match, so I stopped treating "fixed" as the end and started treating my own eyes as the test.
Then Antigravity froze, more than once, and I moved to Kiro, an agentic IDE. Switching didn't mean starting over. The audit findings, a reviewable pull request, and the test expectations gave Kiro something concrete to check instead of a conversation to reconstruct.
Who did what, by the end:
Even after tests passed, my screenshots kept finding things. Story text was bleeding into the question box. That one turned out to be measurable: a text block that needed 42 pixels of height was getting squeezed into about 13. The fix let it keep its natural height, and a regression test now checks for that collision.
The final pass, PR #3:
Kiro confirmed the live site is serving the exact merge commit it reviewed, not just a green "Ready" badge.
Why Does Open Innovation Matter?
An ofrenda is built to be passed down. I wanted the model inside this one to be something that could be passed down too.
For this version I called Gemma through Google's hosted API, because I had two days. But Gemma's weights are public, which means the thing answering questions about my altar is a model anyone can download, run on their own machine, and inspect. If the hosted endpoint changes its terms or disappears, the altar doesn't have to go quiet. With a closed model, I wouldn't have that option.
It also matters for who gets to build these. A community group, a cultural center, a teacher in a classroom with no budget for API bills could run a model like this locally and keep their own families' stories on their own hardware. I didn't build that version. I'd like to.
My Agent Session
I don't have a DevRelay embed for this one, but I do have receipts.
After the freezes, I opened a new Antigravity session on Gemini 4 Argon, a trial model, and asked it to look back at what went wrong. It dug through its own local conversation logs and reported which model each session had run on:
It found two traps. In one, the agent started a preview server in the background and then ended its turn waiting for it to finish, which a running server doesn't do on its own. In the other, on Windows PowerShell, curl is an alias for a different command, and a flag that looks harmless left it stuck waiting on input.
Its theory was that the low reasoning effort in the first two sessions made the first trap easier to fall into. That is Antigravity's read on two sessions, not a proven cause. Then it wrote itself three rules it says it now follows in every session: never end a turn waiting on a background server, always call curl.exe with a 10-second limit, and never pipe binary files through PowerShell's input. No black box there. I loved that.
The Spirit of It
Día de los Muertos is shared remembrance. Families build altars to welcome home the memories of the people they have lost, and the traditions get passed on, from one generation to the next and from one community to another.
The moment it landed for me was that water answer. A model, grounded on what I chose to give it, told a stranger that the way you place a glass of water depends on your family and your community. That is what I have learned over the years, and it came back out of the machine in Spanish.
This week wasn’t about agents or tests for me. I got to take something I have loved since I was 17 and pass a little of it on to a community I love, one made of people from many cultures, with many ways of remembering. A small offering, rough at the edges, built fast. Built with care.
If you build an ofrenda this year, I’d love to hear what goes on yours. And if your family remembers its people in a different way, I’d love to hear about that too.
Prize Categories
-
Best Use of Gemma: Gemma 4 (
gemma-4-26b-a4b-it) answers visitor questions in English and Spanish, grounded on a server-controlled curated knowledge base, with a labeled non-AI fallback.
What it can't do
- The curated fallback was tested by forcing failures locally, not by breaking production.
- Mobile and tablet checks ran in Chromium emulation, not on physical devices.
- The rate limiter is per server instance, so it is best effort, not global.
Seven offerings is a beginning. A family altar holds far more than this one does.
Quick context if you are new here. I work in the California courts, running court operations for the county. I started building with AI in July 2025 and I have been learning in public ever since. I don't write the code. I direct, the agents generate, I validate and decide. I build the Clew Suite, a set of civic tech tools for making complex systems easier to inspect.
Built with AI assistance in October 2026. Cultural content draws on the institutions linked above. Traditions vary by community and family, so treat this as a starting point, not a rulebook.
AI Assisted. Human Approved. Powered by NLP.








Top comments (0)