<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shubhi Merchant</title>
    <description>The latest articles on DEV Community by Shubhi Merchant (@shubhi_merchant_0c115b09e).</description>
    <link>https://dev.to/shubhi_merchant_0c115b09e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4165747%2F780bfed0-3a56-425c-9628-1bbcf709c2c6.png</url>
      <title>DEV Community: Shubhi Merchant</title>
      <link>https://dev.to/shubhi_merchant_0c115b09e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shubhi_merchant_0c115b09e"/>
    <language>en</language>
    <item>
      <title>Ofrenda of the Machines: Remembering the Ghosts We Left in Space</title>
      <dc:creator>Shubhi Merchant</dc:creator>
      <pubDate>Sat, 31 Oct 2026 18:14:47 +0000</pubDate>
      <link>https://dev.to/shubhi_merchant_0c115b09e/ofrenda-of-the-machines-remembering-the-ghosts-we-left-in-space-291g</link>
      <guid>https://dev.to/shubhi_merchant_0c115b09e/ofrenda-of-the-machines-remembering-the-ghosts-we-left-in-space-291g</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Ofrenda of the Machines is an interactive 3D memorial for spacecraft that explored space and are now inactive, lost or still travelling.&lt;/p&gt;

&lt;p&gt;I read &lt;strong&gt;"Ghost in the Machine"&lt;/strong&gt; in a way that suits a memorial. A ghost is what remains after something is gone, and these machines leave a trace of that kind: Opportunity's last transmission from Mars, Voyager 1's signal from interstellar space, and a lander that went silent without anyone being sure why. The "ghost" in my machine is the voice that tells their stories, and the project is built around catching that voice when it makes things up.&lt;/p&gt;

&lt;p&gt;The idea comes from the ofrenda, a memorial made to remember people who are no longer physically present. It is about keeping someone's story alive by arranging names, objects and memories in one place. I applied that to space machines and sorted them into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resting:&lt;/strong&gt; machines whose missions have ended.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restless:&lt;/strong&gt; machines that are still travelling or operating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lost:&lt;/strong&gt; machines that were lost, or whose missions ended unexpectedly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My first three machines are Opportunity, the Mars rover (Resting), Voyager 1, the interstellar probe (Restless), and Mars Polar Lander (Lost). Each story is available at a younger and an older reading level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Live site: &lt;a href="https://merchantshubhi.github.io/ofrenda/" rel="noopener noreferrer"&gt;https://merchantshubhi.github.io/ofrenda/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fco1l28gexooldziww73s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fco1l28gexooldziww73s.png" alt="The 3D altar: three floating islands, one for each category, with a glowing candle and name label for each spacecraft" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1colez9qlhpz0h0096l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi1colez9qlhpz0h0096l.png" alt="The page that introduces the three categories: Resting, Restless and Lost" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo is a floating 3D altar. You drag to look around, scroll or pinch to zoom, and choose a spacecraft's name to open its panel. The panel covers what the machine was, what it found or did, how it worked, a fun fact and its sources, and you can read the story at a younger or older reading level. A List view is available as an alternative to the 3D altar.&lt;/p&gt;

&lt;p&gt;The stories were generated ahead of time with Gemma and saved into &lt;code&gt;machines.json&lt;/code&gt;, so the live site doesn't call a model. There is nothing to install, and the site makes no network requests while running.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not affiliated with NASA. The illustrations are original and simplified.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/merchantshubhi" rel="noopener noreferrer"&gt;
        merchantshubhi
      &lt;/a&gt; / &lt;a href="https://github.com/merchantshubhi/ofrenda" rel="noopener noreferrer"&gt;
        ofrenda
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An interactive, AI-powered digital memorial celebrating robotic spacecraft through 3D experiences, verified mission facts, and age-adaptive storytelling.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ofrenda of the Machines&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Stories for the spacecraft we left behind.&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Ofrenda of the Machines is an interactive, AI-powered digital memorial dedicated to robotic spacecraft that have explored our Solar System and beyond.&lt;/p&gt;
&lt;p&gt;Some machines have completed their missions and now rest on distant worlds. Others continue travelling through interstellar space. Some were lost before they could fulfil their missions.&lt;/p&gt;
&lt;p&gt;This project gives each of them a place to be remembered.&lt;/p&gt;
&lt;p&gt;Through an interactive 3D experience, visitors can explore spacecraft, discover their missions and scientific contributions, and read their stories in two age-appropriate formats.&lt;/p&gt;
&lt;p&gt;The project combines &lt;strong&gt;human-verified scientific information, locally generated AI storytelling, and an interactive 3D frontend&lt;/strong&gt; to create an educational experience that is both informative and reflective.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Table of Contents&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#project-overview" rel="noopener noreferrer"&gt;Project Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#core-features" rel="noopener noreferrer"&gt;Core Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#system-architecture" rel="noopener noreferrer"&gt;System Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#how-the-system-works" rel="noopener noreferrer"&gt;How the System Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#project-structure" rel="noopener noreferrer"&gt;Project Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#technology-stack" rel="noopener noreferrer"&gt;Technology Stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#adding-a-new-spacecraft" rel="noopener noreferrer"&gt;Adding a New Spacecraft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#installation-and-setup" rel="noopener noreferrer"&gt;Installation and Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#running-the-project" rel="noopener noreferrer"&gt;Running the Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/merchantshubhi/ofrenda#testing" rel="noopener noreferrer"&gt;Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/merchantshubhi/ofrenda#data-integrity-and-safety" rel="noopener noreferrer"&gt;Data Integrity&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/merchantshubhi/ofrenda" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The repo is split into three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data/machines/&lt;/code&gt; holds the hand-verified JSON fact sheets, one per spacecraft.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ghosts/&lt;/code&gt; is the Python backend: &lt;code&gt;schema.py&lt;/code&gt; validates the fact sheets, &lt;code&gt;llm.py&lt;/code&gt; talks to Gemma through Ollama, &lt;code&gt;verify.py&lt;/code&gt; checks the generated stories, and &lt;code&gt;build.py&lt;/code&gt; runs the pipeline and writes &lt;code&gt;site/generated/machines.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;site/&lt;/code&gt; is the Three.js frontend. Three.js and OrbitControls are vendored in &lt;code&gt;site/vendor/&lt;/code&gt;, so there is no CDN dependency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To rebuild the stories, run &lt;code&gt;python -m ghosts.build --model gemma4:e4b&lt;/code&gt;. To run the site locally, run &lt;code&gt;python -m http.server 8000 --directory site&lt;/code&gt;. The README has the full setup and covers the tests: pytest for the backend, Node's test runner for the frontend and Puppeteer browser checks.&lt;/p&gt;

&lt;p&gt;Adding a new spacecraft means adding a fact sheet and an entry in &lt;code&gt;site/machine-config.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The data flow is a chain:&lt;/p&gt;

&lt;p&gt;Human-verified fact sheets → Python validation → local LLM → story generation → verification → generated JSON → frontend&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fact sheets.&lt;/strong&gt; Each machine has a JSON fact sheet containing information I verified by hand, with sources. These are the single source of truth. Python validates them before anything else runs, so an incomplete sheet is caught early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Story generation.&lt;/strong&gt; I use Ollama to run a local Gemma model, with no hosted AI API involved. It receives one fact sheet and writes a short first-person story at two reading levels. It is told to rephrase the facts, not add to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification.&lt;/strong&gt; A verification module checks each generated story against its fact sheet: every number, date and proper name has to appear in the sheet, and the text can't contain anything scary for a young reader. If a story fails, the model gets one retry. If it fails again, a fallback builds the story from deterministic templates filled with fact-sheet data. This means every machine always has a story, even when the model gives me something I can't trust.&lt;/p&gt;

&lt;p&gt;This happened in my real build. Of the six stories (three machines, two levels each), five came from Gemma. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuul0phw9er5iszcyrwjo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuul0phw9er5iszcyrwjo.png" alt="Voyager 1 verification image" width="800" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The older-level story for Voyager 1 failed verification, so the site shows the template version instead. Each story on the page carries a short note saying whether it was written by the local AI or retold directly from the facts, so a reader can tell which is which.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build and frontend.&lt;/strong&gt; A build module writes everything to &lt;code&gt;site/generated/machines.json&lt;/code&gt;. The Three.js frontend reads only that file (plus a small config that says what 3D shape each machine gets). It never talks to the model or the verifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline-first.&lt;/strong&gt; Assets are local, the model is local and there are no CDN dependencies. Once built, the site doesn't need the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does it work with the lights and the internet off?&lt;/strong&gt; Yes, by design. The model runs locally through Ollama, the assets are local and there are no CDN dependencies. The Three.js frontend reads a single file, &lt;code&gt;site/generated/machines.json&lt;/code&gt;, so the finished memorial needs neither a network connection nor a running model. A closed API would have broken this. If story generation depended on a hosted model, the site could not be built offline, and the project would depend on someone else's servers being up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it keep my data on my own machine?&lt;/strong&gt; Nothing in this pipeline is sent to an outside service. I should be honest that privacy wasn't my main reason for going local. The facts about Opportunity, Voyager 1 and Mars Polar Lander are public. But the same setup would work for something personal. An ofrenda is a personal kind of memorial, and I can see someone building one for their own family with a local model so the photos and stories never leave their device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it locked inside a service that might disappear?&lt;/strong&gt; This is where the open approach mattered most to me. Gemma is an open-weight model, so I download the weights and keep them. Hosted models get updated, renamed or retired, and when that happens the same prompt can start producing different stories. With a local model I control which version I use, and I can regenerate stories whenever I want. The generated JSON is also a plain file that I own, so even the model is not needed to keep the memorial running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma is the model behind the generated stories in Ofrenda of the Machines. I run &lt;code&gt;gemma4:e4b&lt;/code&gt; locally through Ollama, with no hosted API. It writes each machine's story from a hand-verified fact sheet, and a verification module checks the output before it reaches the site. Without Gemma there would be no generated stories, only the template fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Missing, and What I'd Do Next
&lt;/h2&gt;

&lt;p&gt;I built this on my own, in a limited amount of time. I'd rather be clear about what exists than let the post suggest it's bigger than it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What exists now.&lt;/strong&gt; Three machines with hand-verified fact sheets, locally generated stories at two reading levels, a verification step with a template fallback, and a 3D frontend built with Three.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I'd add with more time.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More machines.&lt;/strong&gt; Three is enough to show the three categories, but not enough to make it feel like a real memorial. Each new machine would start the same way, with a human-verified fact sheet, since the data is what limits growth here. Writing and checking sources by hand takes time. The frontend already arranges several machines per category in an arc, but I haven't filled it yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Richer interaction.&lt;/strong&gt; I'd like visitors to have more ways to explore the machines and their stories, such as a Moon and Mars map.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stronger verification.&lt;/strong&gt; The verifier lowers the chance of a wrong story reaching the site, but it doesn't guarantee correctness. A story can pass the checks and still have a loose turn of phrase, and more time would let me test it against more cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are built yet. They're what I'd work on next.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
