<?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: Naveen Gopalakrishna</title>
    <description>The latest articles on DEV Community by Naveen Gopalakrishna (@naveenneog).</description>
    <link>https://dev.to/naveenneog</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%2F4023553%2Fdba2483f-64d2-4c6d-8481-753abf1b2fe4.jpg</url>
      <title>DEV Community: Naveen Gopalakrishna</title>
      <link>https://dev.to/naveenneog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naveenneog"/>
    <language>en</language>
    <item>
      <title>cordless: Managing Remote Terminals &amp; Coding Agents From My Phone, Like Browser Tabs</title>
      <dc:creator>Naveen Gopalakrishna</dc:creator>
      <pubDate>Fri, 10 Jul 2026 18:45:34 +0000</pubDate>
      <link>https://dev.to/naveenneog/cordless-managing-remote-terminals-coding-agents-from-my-phone-like-browser-tabs-24m6</link>
      <guid>https://dev.to/naveenneog/cordless-managing-remote-terminals-coding-agents-from-my-phone-like-browser-tabs-24m6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — &lt;code&gt;cordless&lt;/code&gt; is a tiny &lt;strong&gt;Node daemon&lt;/strong&gt; on your dev box that owns real terminal sessions (a shell, or &lt;code&gt;claude&lt;/code&gt; / &lt;code&gt;codex&lt;/code&gt;), plus a &lt;strong&gt;phone app&lt;/strong&gt; that attaches to them &lt;strong&gt;like browser tabs&lt;/strong&gt;. Close the app, switch networks, come back later — your sessions are still running and &lt;strong&gt;replay exactly where you left off&lt;/strong&gt;. I designed it in a running conversation with &lt;strong&gt;GPT-5.6 Sol&lt;/strong&gt; on Azure, built it with &lt;strong&gt;GitHub Copilot CLI&lt;/strong&gt;, and verified it in a &lt;strong&gt;real browser &lt;em&gt;and&lt;/em&gt; a real Android emulator&lt;/strong&gt;. Live: &lt;strong&gt;&lt;a href="https://naveenneog.github.io/cordless/" rel="noopener noreferrer"&gt;naveenneog.github.io/cordless&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://naveenneog.github.io/cordless/" rel="noopener noreferrer"&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%2Futf7tx2101eidx75jyo7.png" alt="cordless — remote terminals in your pocket" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I keep leaving long-running coding-agent sessions on my dev box — a &lt;code&gt;claude&lt;/code&gt; chewing through a refactor, a build, a shell mid-task — and then walking away from the keyboard. I wanted to &lt;strong&gt;check on them and steer them from my phone&lt;/strong&gt;, the way I flip between browser tabs. Not SSH-in-a-box; something that treats each agent session as a first-class &lt;strong&gt;tab&lt;/strong&gt; that keeps living when my phone sleeps. So I built &lt;strong&gt;cordless&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent sessions.&lt;/strong&gt; The PTYs live in the daemon. Your phone disconnecting, backgrounding, or hopping from Wi‑Fi to cellular doesn't kill anything. Reconnecting &lt;strong&gt;replays from your last-seen byte&lt;/strong&gt; — or a full-screen snapshot if you were away a while.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tabs for terminals.&lt;/strong&gt; Run several Claude Code / Codex / shell sessions at once and switch instantly, with an unread dot when a background session produces output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Touch-first.&lt;/strong&gt; A real terminal is unusable with thumbs without help, so there's an on-screen &lt;strong&gt;key bar&lt;/strong&gt;: Esc, Tab, Ctrl/Alt (one-shot latches), arrows, Ctrl‑C/D, pipes, and paste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reach it from anywhere.&lt;/strong&gt; Tailscale is the recommended path; same‑Wi‑Fi LAN also works. No ports exposed to the internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web &lt;em&gt;or&lt;/em&gt; native.&lt;/strong&gt; Install the PWA straight from your phone browser, or grab the Android APK.&lt;/li&gt;
&lt;/ul&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%2F16gsb7lz3itmfihj7ft2.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%2F16gsb7lz3itmfihj7ft2.png" alt="pairing screen" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&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%2Fukl90xp9fvpq3f76tk4s.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%2Fukl90xp9fvpq3f76tk4s.png" alt="connected terminal with the new-session sheet" width="800" height="1733"&gt;&lt;/a&gt;&lt;br&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%2Fjsb2hj0hm42std0zglu4.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%2Fjsb2hj0hm42std0zglu4.png" alt="touch key bar" width="800" height="1733"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How it was built
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Designed in tandem with GPT‑5.6 Sol
&lt;/h3&gt;

&lt;p&gt;The interesting part of this build is that I didn't architect it alone. I kept a &lt;strong&gt;stateful conversation&lt;/strong&gt; with &lt;strong&gt;GPT‑5.6 Sol&lt;/strong&gt; (deployed on my Azure AI Foundry) open the entire time — not one-off prompts, but a running transcript so it stayed consistent with every prior decision. Sol produced the initial architecture, and then, crucially, &lt;strong&gt;reviewed my actual code&lt;/strong&gt;. That review earned its keep.&lt;/p&gt;
&lt;h3&gt;
  
  
  The agent: node-pty, not tmux
&lt;/h3&gt;

&lt;p&gt;Each session is a real pseudo-terminal spawned with &lt;strong&gt;&lt;code&gt;node-pty&lt;/code&gt;&lt;/strong&gt; (ConPTY on Windows — which, pleasingly, built on Node 26 first try; a Unix PTY on macOS/Linux). I deliberately &lt;strong&gt;did not&lt;/strong&gt; use tmux: the daemon is &lt;em&gt;already&lt;/em&gt; the multiplexer, and tmux adds nested-terminal state, sizing quirks, and a Windows install problem. Every session is a PTY &lt;strong&gt;plus&lt;/strong&gt; a headless &lt;code&gt;@xterm/headless&lt;/code&gt; mirror (for snapshots) &lt;strong&gt;plus&lt;/strong&gt; an 8 MiB &lt;strong&gt;replay ring&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The subtle bug Sol caught here: &lt;code&gt;@xterm/headless&lt;/code&gt;'s &lt;code&gt;write()&lt;/code&gt; is asynchronous, and &lt;code&gt;reset()&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; ordered behind queued writes. So I route every terminal write and every snapshot through &lt;strong&gt;one per-session op queue&lt;/strong&gt;, and assign a batch's sequence number &lt;strong&gt;inside the write callback&lt;/strong&gt; — which keeps the replay ring, the sequence counter, and &lt;code&gt;serialize()&lt;/code&gt; perfectly consistent. Output is coalesced into ~16 ms / 32 KiB batches, one sequence number per batch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// one op queue per session; seq assigned only after the bytes are parsed&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_queueOp&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_nextSeq&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_pushRing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// ring, counter and headless mirror advance together&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That consistency is what makes &lt;strong&gt;reconnect-with-replay&lt;/strong&gt; honest: attach with your last &lt;code&gt;seq&lt;/code&gt;, and the server either replays the ring from there or, if it's rolled past, serializes the headless terminal into a single reset snapshot. tmux-style survival, without tmux.&lt;/p&gt;
&lt;h3&gt;
  
  
  The client, and the 12 bugs the review caught
&lt;/h3&gt;

&lt;p&gt;The app is Vite + React + &lt;strong&gt;&lt;a href="https://xtermjs.org/" rel="noopener noreferrer"&gt;xterm.js&lt;/a&gt;&lt;/strong&gt;, served by the daemon itself so the PWA is same-origin. I wrote the connection layer, thought it was solid… and handed the whole file to Sol for review. It came back with &lt;strong&gt;twelve real issues&lt;/strong&gt; — not style nits, actual races:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stale-epoch writes &lt;strong&gt;duplicating output&lt;/strong&gt; after a reconnect,&lt;/li&gt;
&lt;li&gt;the duplicate-frame check gating on the wrong counter (applied vs received seq),&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;duplicate-attach&lt;/strong&gt; and detach-during-attach races,&lt;/li&gt;
&lt;li&gt;an ack timer that &lt;strong&gt;leaked across reconnects&lt;/strong&gt; and cleared a fresh ack,&lt;/li&gt;
&lt;li&gt;Ctrl/Alt latches that could get &lt;strong&gt;stuck&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;"close tab" silently &lt;strong&gt;undone&lt;/strong&gt; by the next session-list poll.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix pattern throughout: give every socket a &lt;strong&gt;connection epoch&lt;/strong&gt; that every handler, timer, and promise verifies; serialize each tab's writes through an &lt;strong&gt;apply-chain&lt;/strong&gt;; and use &lt;strong&gt;generation counters&lt;/strong&gt; for attach/detach and resize. Boring, correct, and exactly the kind of thing a fresh reviewer spots that the author's brain has already glossed over.&lt;/p&gt;
&lt;h3&gt;
  
  
  Security, baked in from the start
&lt;/h3&gt;

&lt;p&gt;Because a paired device gets &lt;strong&gt;shell access to my machine&lt;/strong&gt;, security couldn't be an afterthought:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-device tokens&lt;/strong&gt;, only their SHA‑256 hashes stored on the box; revoke any device by id.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-use, rate-limited QR pairing&lt;/strong&gt; — and the pairing secret rides in the URL &lt;strong&gt;fragment&lt;/strong&gt;, so it's never sent to (or logged by) the server.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Origin allowlist&lt;/strong&gt; on the WebSocket and pairing endpoints (blocks malicious web pages / DNS-rebinding), a strict &lt;strong&gt;CSP&lt;/strong&gt; (&lt;code&gt;script-src 'self'&lt;/code&gt;, no inline JS), and &lt;code&gt;no-store&lt;/code&gt; on every credential-bearing response.&lt;/li&gt;
&lt;li&gt;The daemon &lt;strong&gt;warns if you run it as root/Administrator&lt;/strong&gt; and binds with a clear least-privilege note.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a small automated suite that asserts all of this (cross-origin pairing → 403, cross-origin WebSocket → rejected, headers present) so it can't quietly regress.&lt;/p&gt;
&lt;h3&gt;
  
  
  The bug only a real emulator could find
&lt;/h3&gt;

&lt;p&gt;Here's my favorite part. Everything worked perfectly as a &lt;strong&gt;PWA&lt;/strong&gt; — because the browser served the app and the agent from the &lt;strong&gt;same origin&lt;/strong&gt;. Then I packaged the Android APK with &lt;strong&gt;Capacitor&lt;/strong&gt; and installed it on an emulator. Pairing failed instantly: &lt;strong&gt;"Failed to fetch."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cause was genuinely educational. Inside a Capacitor WebView the app's origin is &lt;code&gt;http://localhost&lt;/code&gt;, so talking to the agent is &lt;strong&gt;cross-origin&lt;/strong&gt; — which triggers a &lt;strong&gt;CORS preflight&lt;/strong&gt; my server had never needed to handle, because the same-origin PWA never sent one. The fix was to add CORS &lt;strong&gt;scoped to the existing Origin allowlist&lt;/strong&gt; (echo &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; for allowed origins, answer the &lt;code&gt;OPTIONS&lt;/code&gt; preflight, and — Sol's catch — answer the &lt;strong&gt;Private Network Access&lt;/strong&gt; preflight that Chromium sends when reaching a LAN/Tailscale address). A bug that would have broken &lt;strong&gt;every&lt;/strong&gt; native-app user, invisible until I drove the real APK.&lt;/p&gt;

&lt;p&gt;After the fix, the emulator paired, connected over WebSocket, attached a session, rendered PowerShell output, and &lt;strong&gt;replayed full session history on reconnect&lt;/strong&gt; — the whole loop, natively.&lt;/p&gt;
&lt;h3&gt;
  
  
  Shipping it
&lt;/h3&gt;

&lt;p&gt;The Android APK is built by &lt;strong&gt;GitHub Actions on every &lt;code&gt;v*&lt;/code&gt; tag&lt;/strong&gt; (debug-signed, &lt;code&gt;androidScheme: http&lt;/code&gt; + cleartext so &lt;code&gt;ws://&lt;/code&gt; to a Tailscale IP works) and attached straight to the release. The landing page is GitHub Pages. Networking is &lt;strong&gt;Tailscale-first&lt;/strong&gt;: WireGuard encrypts the hop, you get a stable &lt;code&gt;*.ts.net&lt;/code&gt; name, and port 7443 never touches the public internet.&lt;/p&gt;
&lt;h2&gt;
  
  
  The good
&lt;/h2&gt;

&lt;p&gt;Sessions that &lt;strong&gt;outlive the client&lt;/strong&gt; turn a phone from a read-only status screen into a real remote control for long-running agents. Check a &lt;code&gt;claude&lt;/code&gt; refactor from the sofa, unstick a build on the train, tap Ctrl‑C on a runaway process — then walk back to your desk and the exact same sessions are there in the browser. And because the whole thing is token-gated, Tailscale-scoped, and never exposed publicly, it stays &lt;em&gt;yours&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The meta-lesson: &lt;strong&gt;pairing a builder (me, via Copilot CLI) with a dedicated reviewer (GPT‑5.6 Sol) in a persistent conversation&lt;/strong&gt; produced noticeably better engineering than either alone — the 12-bug review and the CORS catch are things I'd have shipped without. And testing on a &lt;strong&gt;real emulator, not just a browser&lt;/strong&gt; is what turned "looks done" into "actually works."&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;▶️ &lt;strong&gt;Live / install the PWA:&lt;/strong&gt; &lt;a href="https://naveenneog.github.io/cordless/" rel="noopener noreferrer"&gt;naveenneog.github.io/cordless&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Android APK:&lt;/strong&gt; &lt;a href="https://github.com/naveenneog/cordless/releases/latest" rel="noopener noreferrer"&gt;github.com/naveenneog/cordless/releases/latest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/naveenneog/cordless" rel="noopener noreferrer"&gt;github.com/naveenneog/cordless&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&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/naveenneog" rel="noopener noreferrer"&gt;
        naveenneog
      &lt;/a&gt; / &lt;a href="https://github.com/naveenneog/cordless" rel="noopener noreferrer"&gt;
        cordless
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Manage many remote terminal &amp;amp; coding-agent (Claude Code, Codex) sessions from your phone — like browser tabs. Node agent + PWA/Android client.
    &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;cordless&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Manage many remote terminal / coding-agent sessions from your phone — like browser tabs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;cordless runs a small daemon on your dev box or laptop that owns real PTY sessions (a shell, or
&lt;code&gt;claude&lt;/code&gt; / &lt;code&gt;codex&lt;/code&gt;), and a mobile web app that attaches to them like browser tabs. Sessions keep
running when you disconnect; reconnecting replays exactly where you left off.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Site:&lt;/strong&gt; &lt;a href="https://naveenneog.github.io/cordless/" rel="nofollow noopener noreferrer"&gt;naveenneog.github.io/cordless&lt;/a&gt; · &lt;strong&gt;Download:&lt;/strong&gt; &lt;a href="https://github.com/naveenneog/cordless/releases/latest" rel="noopener noreferrer"&gt;latest APK&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/naveenneog/cordless/docs/screenshot.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fnaveenneog%2Fcordless%2FHEAD%2Fdocs%2Fscreenshot.png" alt="cordless" width="820"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent sessions&lt;/strong&gt; — PTYs survive phone disconnects, network switches, and app backgrounding
Reconnect replays from your last-seen byte (or a full-screen snapshot if you were away too long).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tabs for terminals&lt;/strong&gt; — run several Claude Code / Codex / shell sessions at once, switch instantly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Touch-first&lt;/strong&gt; — an on-screen key bar (Esc, Tab, Ctrl/Alt, arrows, Ctrl-C/D, pipes, paste) makes a
real terminal usable with thumbs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reach it from anywhere&lt;/strong&gt; — Tailscale is the recommended path; same-Wi-Fi LAN also works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure by&lt;/strong&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/naveenneog/cordless" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Part of the #AI4Good series. Built one day at a time. — &lt;a href="https://github.com/naveenneog" rel="noopener noreferrer"&gt;@naveenneog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai4good</category>
      <category>terminal</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI4Good: I Shipped an AI-for-Good App Every Few Days — Here's the Build Story</title>
      <dc:creator>Naveen Gopalakrishna</dc:creator>
      <pubDate>Fri, 10 Jul 2026 08:14:57 +0000</pubDate>
      <link>https://dev.to/naveenneog/ai4good-i-shipped-an-ai-for-good-app-every-few-days-heres-the-build-story-15f6</link>
      <guid>https://dev.to/naveenneog/ai4good-i-shipped-an-ai-for-good-app-every-few-days-heres-the-build-story-15f6</guid>
      <description>&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%2F3ls9q6ir49rr5eioo88u.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%2F3ls9q6ir49rr5eioo88u.png" alt="AI4Good — eight apps, eighteen days, AI for good" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;#AI4Good&lt;/strong&gt; started as a dare to myself: &lt;em&gt;can I use AI to build and ship something genuinely useful — for kids, for health, for our culture — one build at a time?&lt;/em&gt; Eighteen days later there were &lt;strong&gt;eight live apps&lt;/strong&gt;, each free, each on GitHub Pages. This is the story of how they came to be, in the order they were born — and &lt;strong&gt;each has its own deep-dive post&lt;/strong&gt; on the &lt;a href="https://naveenneog.github.io/AI4Good/" rel="noopener noreferrer"&gt;AI4Good blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm &lt;strong&gt;Naveen Gopalakrishna&lt;/strong&gt; — an AI Global Black Belt at Microsoft by day, a compulsive builder by night. The tooling that made this pace possible was &lt;strong&gt;GitHub Copilot CLI in autopilot mode&lt;/strong&gt; (the agent researches, writes code, runs builds, and renders assets autonomously) on top of &lt;strong&gt;Azure AI&lt;/strong&gt; — &lt;code&gt;gpt-image-2&lt;/code&gt; for art, Azure Speech for narration, and open models from Hugging Face for the heavy lifting. But tools are just tools. The thread that ties these eight together is a question I kept asking: &lt;strong&gt;who does this help?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's walk the timeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jun 22 — PrimeBeats: your music, beautifully local
&lt;/h2&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%2Fmuzpwx8j81axnhuf7ply.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%2Fmuzpwx8j81axnhuf7ply.png" alt="PrimeBeats landing page — your music, beautifully local" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where it started. &lt;a href="https://naveenneog.github.io/PrimeBeats/" rel="noopener noreferrer"&gt;&lt;strong&gt;PrimeBeats&lt;/strong&gt;&lt;/a&gt; is an Amazon-Prime-Music-style player for the songs already on your phone — &lt;strong&gt;background playback&lt;/strong&gt;, &lt;strong&gt;lock-screen controls&lt;/strong&gt;, playlists, an on-device &lt;strong&gt;Smart Radio&lt;/strong&gt;, and a real &lt;strong&gt;equalizer&lt;/strong&gt;. No account, no ads, &lt;strong&gt;100% offline&lt;/strong&gt;. Built with React Native + Expo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; ownership and privacy — your music, zero data, zero tracking, zero cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jun 23 — ActionCut: put a pro video studio in everyone's pocket
&lt;/h2&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%2F0eq2ybiaqpgch4x3epeb.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%2F0eq2ybiaqpgch4x3epeb.png" alt="ActionCut landing page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next build set the tone: take something normally locked behind subscriptions and make it &lt;strong&gt;free and open&lt;/strong&gt;. &lt;a href="https://naveenneog.github.io/ActionCut/" rel="noopener noreferrer"&gt;&lt;strong&gt;ActionCut&lt;/strong&gt;&lt;/a&gt; is a CapCut-class Android video editor — a real &lt;strong&gt;multi-track timeline&lt;/strong&gt;, &lt;strong&gt;LUT color filters&lt;/strong&gt;, GPU effects, audio mixing, and one-tap &lt;strong&gt;export presets&lt;/strong&gt; for every platform. Built in &lt;strong&gt;Kotlin + Jetpack Compose + Media3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; creators on entry-level Android phones get studio-grade editing without a paywall or a watermark. Storytelling shouldn't require a subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jun 26 — KidKat: only the good stuff, for curious kids
&lt;/h2&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%2F9g2w8psrg6s28pichwsv.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%2F9g2w8psrg6s28pichwsv.png" alt="KidKat landing page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the app that named the whole campaign. &lt;a href="https://naveenneog.github.io/KidKat/" rel="noopener noreferrer"&gt;&lt;strong&gt;KidKat&lt;/strong&gt;&lt;/a&gt; plays a &lt;strong&gt;finite, parent-approved stream&lt;/strong&gt; of short educational videos — inside the official YouTube player, using the official Data API, fully &lt;strong&gt;ToS-compliant&lt;/strong&gt;. No infinite feed. No algorithmic rabbit holes. No doomscroll. A parent allowlist decides what's on the menu. Built with &lt;strong&gt;Flutter&lt;/strong&gt; for Android and iOS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; it turns the most anxiety-inducing part of modern parenting — handing a child a screen — into something calm and intentional. &lt;strong&gt;AI for good starts with protecting the youngest users.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jun 27 — NeoFit: health that speaks your language
&lt;/h2&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%2Frehq2cofpqyw7tl4gj6c.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%2Frehq2cofpqyw7tl4gj6c.png" alt="NeoFit landing page — Sehat, simple banayi" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Sehat, simple banayi."&lt;/em&gt; &lt;a href="https://naveenneog.github.io/NeoFit/" rel="noopener noreferrer"&gt;&lt;strong&gt;NeoFit&lt;/strong&gt;&lt;/a&gt; is a science-informed &lt;strong&gt;Indian&lt;/strong&gt; health and fitness app in &lt;strong&gt;22 Indian languages&lt;/strong&gt; — calorie tracking that actually knows &lt;em&gt;dosa, biryani, and gulab jamun&lt;/em&gt;, &lt;strong&gt;on-device food recognition&lt;/strong&gt;, AI-generated food photos, AI exercise videos, and Health Connect sync. Offline-first, &lt;strong&gt;powered by Azure AI&lt;/strong&gt;, Kotlin/Compose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; most fitness apps assume you eat like the West and read English fluently. NeoFit meets 1.4 billion people where they are — in their language, with their food. &lt;strong&gt;Inclusion is a health outcome.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jul 04 — GpsCamera: proof you can trust
&lt;/h2&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%2Fs0eanuvciigri3kvfjm7.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%2Fs0eanuvciigri3kvfjm7.png" alt="GpsCamera landing page — geotag every photo, exactly" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://naveenneog.github.io/GpsCamera/" rel="noopener noreferrer"&gt;&lt;strong&gt;GpsCamera&lt;/strong&gt;&lt;/a&gt; is a fast, native Android camera that &lt;strong&gt;burns your exact location and a live mini-map onto every photo&lt;/strong&gt;, writes standards-compliant &lt;strong&gt;GPS EXIF&lt;/strong&gt;, and files each shot into its own album. Kotlin, Compose, CameraX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; field engineers, insurance surveyors, community reporters, and site inspectors need &lt;strong&gt;verifiable, tamper-evident documentation&lt;/strong&gt;. A trustworthy timestamped-and-geotagged photo is a small tool that quietly protects people.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jul 05 — The Lamp &amp;amp; the Machine: shadow-puppet theatre as an AI film studio
&lt;/h2&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%2Fg6onl0dr1qqwvxke1szl.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%2Fg6onl0dr1qqwvxke1szl.png" alt="The Lamp &amp;amp; the Machine — Togalu Gombeyaata meets generative AI" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://naveenneog.github.io/Sora-Azure-MultiPart-Video-Editing/" rel="noopener noreferrer"&gt;&lt;strong&gt;The Lamp &amp;amp; the Machine&lt;/strong&gt;&lt;/a&gt; turns Karnataka's 500-year-old leather shadow-puppet theatre, &lt;strong&gt;Togalu Gombeyaata&lt;/strong&gt;, into an AI film studio — cinematic scenes from &lt;strong&gt;Sora 2&lt;/strong&gt;, narration from &lt;strong&gt;Azure AI&lt;/strong&gt;, finished with Demucs + FFmpeg, retelling the story of &lt;strong&gt;Kempegowda&lt;/strong&gt;, the founder of Bengaluru.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; generative AI as a &lt;strong&gt;preservation tool&lt;/strong&gt; — putting a fading folk art in front of a new generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jul 06 — Sopāna: a Snakes &amp;amp; Ladders that teaches
&lt;/h2&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%2Fn32cj2a0imxdelqrxhj5.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%2Fn32cj2a0imxdelqrxhj5.png" alt="Sopana landing page — Moksha Patam" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://naveenneog.github.io/Sopana/" rel="noopener noreferrer"&gt;&lt;strong&gt;Sopāna&lt;/strong&gt;&lt;/a&gt; reclaims the game the West calls &lt;em&gt;Snakes &amp;amp; Ladders&lt;/em&gt; and restores its origin — the ancient Indian &lt;strong&gt;Moksha Patam&lt;/strong&gt;, where &lt;strong&gt;every snake is a vice and every ladder a virtue&lt;/strong&gt;. Land on one and the game &lt;strong&gt;animates and reads its meaning aloud&lt;/strong&gt;. Play three ways (a 2D board, a 2.5D cinematic mode, and a 3D camera), across four worlds, with up to four players on one screen. A web PWA plus an Android APK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; it turns a children's game back into what it was designed to be — a gentle lesson in ethics and consequence, wrapped in cultural heritage. &lt;strong&gt;AI narration and art make an 800-year-old teaching tool feel alive again.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Jul 09 — Chaturanga: ancient chess, reborn in glowing 3D
&lt;/h2&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%2Ffhtdbwuxjwxw0ecnzyvd.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%2Ffhtdbwuxjwxw0ecnzyvd.png" alt="Chaturanga landing page — the four-army game of dharma" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://naveenneog.github.io/Chaturanga/" rel="noopener noreferrer"&gt;&lt;strong&gt;Chaturanga&lt;/strong&gt;&lt;/a&gt; is the ancient Indian "game of the four divisions," played with modern chess rules but &lt;strong&gt;authentic piece identities&lt;/strong&gt; — Raja, Mantri, Gaja, Ashva, Ratha, Padati — carved in &lt;strong&gt;real-time glowing 3D&lt;/strong&gt;. It ships with a &lt;strong&gt;teaching AI&lt;/strong&gt; (five levels), a &lt;strong&gt;coach&lt;/strong&gt; that reviews your blunders, an &lt;strong&gt;openings trainer&lt;/strong&gt;, a rotating piece inspector, and a &lt;em&gt;Warrior's Eye&lt;/em&gt; camera. Four worlds, each teaching a moral lesson.&lt;/p&gt;

&lt;p&gt;The pieces themselves are an AI-for-good story: I generated each one from a single concept image using a &lt;strong&gt;free Hugging Face Space&lt;/strong&gt; and headless Blender — the full image-to-3D pipeline is &lt;a href="https://naveenneog.github.io/AI4Good/2026/07/10/image-to-3d-huggingface-blender-copilot/" rel="noopener noreferrer"&gt;its own post here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good:&lt;/strong&gt; a free, offline, no-sign-in chess tutor that also carries culture — learning and heritage in one board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What #AI4Good actually means
&lt;/h2&gt;

&lt;p&gt;Look at all eight together and a pattern appears — not "AI apps," but &lt;strong&gt;apps that use AI to do something good&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Who it serves&lt;/th&gt;
&lt;th&gt;The good&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Jun 22&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/PrimeBeats/" rel="noopener noreferrer"&gt;PrimeBeats&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Listeners&lt;/td&gt;
&lt;td&gt;Own your music — offline, ad-free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jun 23&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/ActionCut/" rel="noopener noreferrer"&gt;ActionCut&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Creators&lt;/td&gt;
&lt;td&gt;Studio-grade editing, free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jun 26&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/KidKat/" rel="noopener noreferrer"&gt;KidKat&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Kids &amp;amp; parents&lt;/td&gt;
&lt;td&gt;Safe, finite, educational screen time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jun 27&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/NeoFit/" rel="noopener noreferrer"&gt;NeoFit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;1.4B Indians&lt;/td&gt;
&lt;td&gt;Health in 22 languages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 04&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/GpsCamera/" rel="noopener noreferrer"&gt;GpsCamera&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Field workers&lt;/td&gt;
&lt;td&gt;Verifiable documentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 05&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/Sora-Azure-MultiPart-Video-Editing/" rel="noopener noreferrer"&gt;The Lamp &amp;amp; the Machine&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Culture&lt;/td&gt;
&lt;td&gt;Preserving a folk art with AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 06&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/Sopana/" rel="noopener noreferrer"&gt;Sopāna&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Families&lt;/td&gt;
&lt;td&gt;Values + heritage, playfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jul 09&lt;/td&gt;
&lt;td&gt;&lt;a href="https://naveenneog.github.io/Chaturanga/" rel="noopener noreferrer"&gt;Chaturanga&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Learners&lt;/td&gt;
&lt;td&gt;Free chess tutor + culture&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The method:&lt;/strong&gt; one build at a time, from a single laptop, with &lt;strong&gt;Copilot CLI autopilot&lt;/strong&gt; doing the heavy lifting and &lt;strong&gt;Azure AI&lt;/strong&gt; + &lt;strong&gt;Hugging Face&lt;/strong&gt; models supplying the intelligence. &lt;strong&gt;The mission:&lt;/strong&gt; put that capability toward education, health, culture, and trust — and give it away.&lt;/p&gt;

&lt;p&gt;This is just the beginning of the campaign. Next up the runway: &lt;strong&gt;Indian Tales&lt;/strong&gt;, a multilingual Togalu Gombe (shadow-puppet) storytelling app to keep a fading folk art alive.&lt;/p&gt;

&lt;p&gt;If you build things, I'd gently challenge you to the same dare: &lt;strong&gt;pick the "who does this help?" first, then let AI help you ship it.&lt;/strong&gt; That's &lt;strong&gt;#AI4Good&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow the journey — &lt;a href="https://github.com/naveenneog" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://www.linkedin.com/in/naveen-gopalakrishna-99863040/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. Built one day at a time.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai4good</category>
      <category>ai</category>
      <category>opensource</category>
      <category>showcase</category>
    </item>
    <item>
      <title>Carving a Glowing 3D Army from Single Images: Hugging Face + Blender, driven by Copilot CLI Autopilot</title>
      <dc:creator>Naveen Gopalakrishna</dc:creator>
      <pubDate>Fri, 10 Jul 2026 07:54:18 +0000</pubDate>
      <link>https://dev.to/naveenneog/carving-a-glowing-3d-army-from-single-images-hugging-face-blender-driven-by-copilot-cli-1700</link>
      <guid>https://dev.to/naveenneog/carving-a-glowing-3d-army-from-single-images-hugging-face-blender-driven-by-copilot-cli-1700</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — One &lt;code&gt;gpt-image-2&lt;/code&gt; concept image per piece → a &lt;strong&gt;free Hugging Face Space&lt;/strong&gt; (&lt;code&gt;tencent/Hunyuan3D-2&lt;/code&gt;) turns it into a raw 3D mesh → &lt;strong&gt;headless Blender&lt;/strong&gt; projects the concept back on as texture → a web-ready &lt;strong&gt;GLB&lt;/strong&gt; that glows in Three.js. No local GPU. All of it — the research, the scripts, the Hugging Face calls, the Blender runs, and &lt;em&gt;this very post and its auto-publish to DEV&lt;/em&gt; — was orchestrated by &lt;strong&gt;GitHub Copilot CLI in autopilot mode&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&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%2Fmvupl6tbarm0iodip7lr.jpg" 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%2Fmvupl6tbarm0iodip7lr.jpg" alt="Glowing gold-vs-purple Chaturanga board" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've been building &lt;strong&gt;&lt;a href="https://naveenneog.github.io/Chaturanga" rel="noopener noreferrer"&gt;Chaturanga&lt;/a&gt;&lt;/strong&gt; — the ancient Indian "game of the four divisions," played with modern chess moves but authentic piece identities (&lt;em&gt;Raja, Mantri, Gaja, Ashva, Ratha, Padati&lt;/em&gt;). The board above is the payoff. The hard part was the &lt;strong&gt;pieces&lt;/strong&gt;: carved-ivory war figures with real silhouettes, in four themed armies, that spin in a WebGL inspector — built for &lt;strong&gt;free, on a laptop with no GPU&lt;/strong&gt;, in a &lt;strong&gt;fully scriptable&lt;/strong&gt; way.&lt;/p&gt;

&lt;p&gt;Here's how that pipeline came together, including the parts that didn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline at a glance
&lt;/h2&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%2Frrkyz7p1pfls2w4o3pkm.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%2Frrkyz7p1pfls2w4o3pkm.png" alt="Image-to-3D pipeline diagram" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four stages, each free and headless:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Concept&lt;/strong&gt; — Azure &lt;code&gt;gpt-image-2&lt;/code&gt; renders a carved-ivory concept per piece (front 3/4, plain background).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image → 3D&lt;/strong&gt; — the concept goes to a &lt;strong&gt;Hugging Face Space&lt;/strong&gt; that runs image-to-3D on donated GPU and returns a mesh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Texture&lt;/strong&gt; — &lt;strong&gt;Blender 5.1&lt;/strong&gt;, headless on CPU, projects the concept image onto the mesh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship&lt;/strong&gt; — export a decimated, textured &lt;strong&gt;GLB&lt;/strong&gt; and load it in Three.js with a little emissive glow.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2 is the heart: image → 3D on a free Hugging Face Space
&lt;/h2&gt;

&lt;p&gt;Instead of buying GPU time, I call &lt;strong&gt;&lt;a href="https://huggingface.co/spaces/tencent/Hunyuan3D-2" rel="noopener noreferrer"&gt;&lt;code&gt;tencent/Hunyuan3D-2&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; through its Gradio API. The Space runs the heavy diffusion-to-mesh step on &lt;strong&gt;free ZeroGPU&lt;/strong&gt;; I just pull back a &lt;code&gt;.glb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;gradio_client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handle_file&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tencent/Hunyuan3D-2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hf_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HF_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# token optional; helps rate limits
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;handle_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refs/raja.jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guidance_scale&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;octree_resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_box_rembg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;randomize_seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/shape_generation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;# geometry only — fast; texture done later in Blender
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# result -&amp;gt; a raw .glb mesh, generated on someone else's GPU, for free
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;No-signup fallback:&lt;/strong&gt; for a path that needs &lt;em&gt;zero&lt;/em&gt; accounts, I keep &lt;strong&gt;&lt;a href="https://huggingface.co/stabilityai/TripoSR" rel="noopener noreferrer"&gt;&lt;code&gt;stabilityai/TripoSR&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; (MIT) running &lt;strong&gt;locally on CPU&lt;/strong&gt; — the only open image-to-3D model I found that's genuinely CPU-feasible on Windows.&lt;/p&gt;
&lt;h3&gt;
  
  
  Discovering Spaces from the terminal
&lt;/h3&gt;

&lt;p&gt;Copilot CLI has the &lt;strong&gt;Hugging Face MCP server&lt;/strong&gt; wired in, so I search the Hub without leaving my shell. Asking for image-to-3D Spaces returns the exact shortlist this project lives on: &lt;code&gt;microsoft/TRELLIS.2&lt;/code&gt; (⭐1.7k), &lt;code&gt;tencent/Hunyuan3D-2&lt;/code&gt; (⭐3.3k, what I use), &lt;code&gt;TencentARC/InstantMesh&lt;/code&gt; (⭐1.6k).&lt;/p&gt;
&lt;h2&gt;
  
  
  From flat concept to spinning figure
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;gpt-image-2&lt;/code&gt; concept on the left, the Hugging Face + Blender result on the right:&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%2Fenjear7gr8t083cnp7v2.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%2Fenjear7gr8t083cnp7v2.png" alt="Raja: concept vs 3D" width="800" height="594"&gt;&lt;/a&gt;&lt;br&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%2Fs62grccu9hbfm0mf1mjz.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%2Fs62grccu9hbfm0mf1mjz.png" alt="Ashva: concept vs 3D" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And on the turntable (Raja · Ashva · Gaja):&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%2Fpevz63zs6h9ew7tbn2e3.gif" 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%2Fpevz63zs6h9ew7tbn2e3.gif" alt="Raja turntable" width="170" height="460"&gt;&lt;/a&gt;&lt;br&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%2F2epg5e2ztpdq1pymdv1m.gif" 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%2F2epg5e2ztpdq1pymdv1m.gif" alt="Ashva turntable" width="301" height="460"&gt;&lt;/a&gt;&lt;br&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%2Ffuh4xkhxxxhehjjd7dwk.gif" 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%2Ffuh4xkhxxxhehjjd7dwk.gif" alt="Gaja turntable" width="378" height="460"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 — Where Blender earns its keep (and where it fights back)
&lt;/h2&gt;

&lt;p&gt;The Hugging Face mesh has a &lt;strong&gt;correct silhouette but a soft surface&lt;/strong&gt;. The crisp carving detail lives in the concept image. So Blender's job is &lt;strong&gt;concept-texture projection&lt;/strong&gt;: orient the mesh, frame an orthographic camera on its front, and set each vertex's UV to that camera's projection — so the concept &lt;em&gt;becomes&lt;/em&gt; the surface.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;blender &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; tooling/blender/texture_project.py &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    raw/raja_hunyuan.glb refs/raja.jpg web/raja.glb preview.png
&lt;span class="c"&gt;# headless CPU: orient -&amp;gt; project concept as texture -&amp;gt; decimate to ~28k faces -&amp;gt; export GLB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The honest limitations I hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Back faces get a mirrored projection.&lt;/strong&gt; Single-camera projection is perfect head-on; the reverse is a mirror. For board pieces seen from above, an acceptable trade — but a trade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blender 4 → 5 broke my material scripts.&lt;/strong&gt; Principled BSDF sockets were renamed: &lt;code&gt;Subsurface Weight&lt;/code&gt; not &lt;code&gt;Subsurface&lt;/code&gt;, &lt;code&gt;Specular IOR Level&lt;/code&gt; not &lt;code&gt;Specular&lt;/code&gt;. &lt;code&gt;KeyError&lt;/code&gt; until I re-bound each one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eevee Next won't render headless on Windows.&lt;/strong&gt; &lt;code&gt;blender -b&lt;/code&gt; with new Eevee crashes with an access violation — it wants a GPU context a background process lacks. &lt;strong&gt;Cycles on CPU&lt;/strong&gt; renders fine; every image here is Cycles-CPU.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No native build allowed.&lt;/strong&gt; Marching cubes runs via &lt;strong&gt;PyMCubes&lt;/strong&gt;, not a compiled extension, to stay laptop-reproducible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The detour:&lt;/strong&gt; I first tried &lt;strong&gt;Blender-only&lt;/strong&gt; procedural modeling (skin modifier, geometry nodes, baked PBR). Fully scriptable, GPU-free… and it produced &lt;strong&gt;primitive blobs after hours&lt;/strong&gt;. Great for &lt;em&gt;finishing&lt;/em&gt;, wrong tool for &lt;em&gt;creating&lt;/em&gt; organic figures. That failure is why the Hugging Face step exists.&lt;/p&gt;
&lt;h2&gt;
  
  
  The tools I tried — and where they hit a wall
&lt;/h2&gt;

&lt;p&gt;Constraints on purpose: &lt;strong&gt;free, Windows, no GPU, scriptable.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;Verdict under my constraints&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hunyuan3D-2 (HF Space)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;image→mesh, free ZeroGPU&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Winner&lt;/strong&gt; — GPU-quality geometry, no local GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;TripoSR&lt;/strong&gt; (local)&lt;/td&gt;
&lt;td&gt;image→mesh, CPU&lt;/td&gt;
&lt;td&gt;✅ Fallback — only CPU-feasible local model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meshy AI&lt;/td&gt;
&lt;td&gt;REST API, 200 credits/mo&lt;/td&gt;
&lt;td&gt;⚠️ Great, but ~6 textured pieces/mo, CC-BY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tripo AI&lt;/td&gt;
&lt;td&gt;REST API, 300 credits/mo&lt;/td&gt;
&lt;td&gt;⚠️ Good, but 15 downloads/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rodin (Hyper3D)&lt;/td&gt;
&lt;td&gt;REST API, pay-on-export&lt;/td&gt;
&lt;td&gt;⚠️ Best detail, but credits burn on download&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InstantMesh / TRELLIS / Wonder3D&lt;/td&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;Not CPU-feasible&lt;/strong&gt; — CUDA-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern: hosted APIs are excellent but &lt;strong&gt;metered&lt;/strong&gt;; the best local models need a &lt;strong&gt;GPU I don't have&lt;/strong&gt;. The escape hatch is a &lt;strong&gt;community-hosted Hugging Face Space&lt;/strong&gt; — the model runs on donated GPU and I pay nothing but a queue wait.&lt;/p&gt;
&lt;h2&gt;
  
  
  The meta-story: built in Copilot CLI autopilot mode
&lt;/h2&gt;

&lt;p&gt;I didn't hand-write most of this. I drove &lt;strong&gt;GitHub Copilot CLI in autopilot mode&lt;/strong&gt; — where the agent runs shell commands, scripts, and tools &lt;strong&gt;autonomously&lt;/strong&gt;, multi-step, without stopping for approval each move:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;research sub-agent&lt;/strong&gt; fanned out across 13 image-to-3D tools and returned a sourced comparison;&lt;/li&gt;
&lt;li&gt;the agent &lt;strong&gt;wrote and ran&lt;/strong&gt; the batch driver, called the Hunyuan3D-2 Space, and retried on ZeroGPU hiccups;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;headless Blender&lt;/strong&gt; runs for texture and the turntables above — it caught the Eevee-headless crash and switched to Cycles itself;&lt;/li&gt;
&lt;li&gt;and &lt;strong&gt;this post&lt;/strong&gt; — rendered GIFs, diagram, prose — plus the &lt;strong&gt;GitHub Actions workflow that auto-published it to DEV&lt;/strong&gt; on push.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Results &amp;amp; what's next
&lt;/h2&gt;

&lt;p&gt;A full &lt;strong&gt;four-world&lt;/strong&gt; army — Kurukshetra, Ramayana, Kalinga, Devasura — each piece a real 3D figure with a themed material and emissive bloom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; the game is live at &lt;strong&gt;&lt;a href="https://naveenneog.github.io/Chaturanga" rel="noopener noreferrer"&gt;naveenneog.github.io/Chaturanga&lt;/a&gt;&lt;/strong&gt;.&lt;/p&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/naveenneog" rel="noopener noreferrer"&gt;
        naveenneog
      &lt;/a&gt; / &lt;a href="https://github.com/naveenneog/Chaturanga" rel="noopener noreferrer"&gt;
        Chaturanga
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Chaturanga — the ancient Indian four-army game in real 3D: a teaching chess AI with levels, an openings trainer, a piece inspector, a Warrior's Eye camera, and two themed worlds. Web + Android.
    &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;Chaturanga — the four-army game of dharma&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Chaturanga&lt;/em&gt; is the ancient Indian ancestor of chess — the game of the &lt;strong&gt;four army
divisions&lt;/strong&gt;. This is a real-3D, mobile-first take on it: authentic Chaturanga piece
&lt;strong&gt;identities&lt;/strong&gt; (Raja, Mantri, Ratha, Gaja, Ashva, Padati) that move by &lt;strong&gt;modern chess
rules&lt;/strong&gt;, where every world teaches a moral through its pieces, and a &lt;strong&gt;teaching AI&lt;/strong&gt;
explains the game as you play.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;▶ Play:&lt;/strong&gt; &lt;a href="https://naveenneog.github.io/Chaturanga/" rel="nofollow noopener noreferrer"&gt;https://naveenneog.github.io/Chaturanga/&lt;/a&gt; &amp;nbsp;·&amp;nbsp; &lt;strong&gt;⬇ Android APK:&lt;/strong&gt; see &lt;a href="https://github.com/naveenneog/Chaturanga/releases/latest" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/naveenneog/Chaturanga/docs/img/ramayana.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fnaveenneog%2FChaturanga%2FHEAD%2Fdocs%2Fimg%2Framayana.png" alt="Lanka — the Ramayana world"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Play the Guru&lt;/strong&gt; — an alpha-beta chess AI with &lt;strong&gt;5 difficulty levels&lt;/strong&gt; (Padati → Mantri)
running in a Web Worker so it stays smooth on phones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A coach&lt;/strong&gt; — a &lt;strong&gt;Hint&lt;/strong&gt; that names the best move and &lt;em&gt;why&lt;/em&gt;, and a blunder review that
gently flags mistakes and shows the stronger move.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Openings trainer&lt;/strong&gt; — six classic openings (Italian, Ruy López, Sicilian, French
Queen's Gambit, King's Indian) walked &lt;strong&gt;move-by-move&lt;/strong&gt; with a narrated…&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/naveenneog/Chaturanga" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Steal the pipeline:&lt;/strong&gt; &lt;code&gt;gpt-image-2&lt;/code&gt; → &lt;code&gt;tencent/Hunyuan3D-2&lt;/code&gt; Space → Blender projection → GLB. Free, no GPU, every step a script. For game assets, product mockups, or avatars, a free Hugging Face Space is the cheat code — and Copilot CLI autopilot stitches it all together.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built one day at a time. — &lt;a href="https://github.com/naveenneog" rel="noopener noreferrer"&gt;@naveenneog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>huggingface</category>
      <category>blender</category>
      <category>ai</category>
      <category>3d</category>
    </item>
  </channel>
</rss>
