<?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: Choi Minho</title>
    <description>The latest articles on DEV Community by Choi Minho (@lenchoi).</description>
    <link>https://dev.to/lenchoi</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%2F4179682%2Ffe98f801-4d99-4631-8570-4326e101db00.png</url>
      <title>DEV Community: Choi Minho</title>
      <link>https://dev.to/lenchoi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lenchoi"/>
    <language>en</language>
    <item>
      <title>I built a live pixel library where vibe coders sit together while their AI works</title>
      <dc:creator>Choi Minho</dc:creator>
      <pubDate>Mon, 12 Oct 2026 15:22:35 +0000</pubDate>
      <link>https://dev.to/lenchoi/i-built-a-live-pixel-library-where-vibe-coders-sit-together-while-their-ai-works-1abl</link>
      <guid>https://dev.to/lenchoi/i-built-a-live-pixel-library-where-vibe-coders-sit-together-while-their-ai-works-1abl</guid>
      <description>&lt;p&gt;Vibe coding alone at night gets lonely. You send a prompt, the agent goes off for a few minutes, and you sit there staring at a terminal.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://vibrary.dev/?ref=devto" rel="noopener noreferrer"&gt;Vibrary&lt;/a&gt;&lt;/strong&gt;: a live pixel-art library where people coding with AI sit together. When you run Claude Code, Codex or Gemini CLI, your character takes a seat and a little bubble shows what your AI is doing — thinking, editing, running tests, shipping. When someone deploys, the whole room claps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxiye7bjnmhmyj1sn6l6t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxiye7bjnmhmyj1sn6l6t.gif" alt="A pixel-art library where characters sit at desks while speech bubbles show what each person's AI is doing" width="560" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The clip is demo mode with sample people. The real library is still small.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This post is about how it's built and the things that went wrong along the way. Almost all of the code was written with Claude Code; I did the planning, the pixel art direction and a lot of testing.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A live room.&lt;/strong&gt; Everyone's AI activity shows up as a character at a desk, in real time, across five floors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy first.&lt;/strong&gt; No code, prompts or file paths ever leave your machine. Only the kind of activity and some numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A verified card for your GitHub README.&lt;/strong&gt; Tokens used, commits, deploys, AI work time — so "I use AI a lot" becomes something you can actually show.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free promotion.&lt;/strong&gt; Register what you built and it shows up on the library's wall display.&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%2F4dyljwb5yjycrjl30z4z.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%2F4dyljwb5yjycrjl30z4z.png" alt="A Vibrary AI builder card on a GitHub profile showing active days, tokens, AI work time, deploys and commits" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with one ugly HTML file
&lt;/h2&gt;

&lt;p&gt;The whole library began as a single HTML file with a canvas: rooms, seats, characters, bubbles, an elevator between floors. Iterating on the &lt;em&gt;feel&lt;/em&gt; was much faster when everything was in one place and I could just refresh.&lt;/p&gt;

&lt;p&gt;When it was time to ship, I didn't rewrite it. Claude wrote a small build script that takes the prototype and turns it into the pieces the Next.js app loads: the engine script, the markup and the CSS. The script also patches a handful of spots for "live mode" (real people instead of sample ones).&lt;/p&gt;

&lt;p&gt;The same script handles translation: every Korean string literal in the engine is replaced from a JSON dictionary at build time, and the build fails loudly if anything is left untranslated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; a throwaway prototype doesn't have to be thrown away. A build step that adapts it is often cheaper than a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Privacy as a hard rule, enforced on both ends
&lt;/h2&gt;

&lt;p&gt;The CLI watches your AI tool's activity locally and only sends something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude_code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"added"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"removed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server, the ingest function keeps &lt;strong&gt;only numeric fields from a fixed list&lt;/strong&gt; and drops everything else, so even a buggy or modified client can't smuggle text through.&lt;/p&gt;

&lt;p&gt;What counts as a "deploy" (for example &lt;code&gt;git push&lt;/code&gt; or &lt;code&gt;gh pr merge&lt;/code&gt;) lives in a rules file on the server. When I found commands being misclassified, I could fix the rules without shipping a new CLI — and the CLI re-scans when the rules version changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Signed auto-updates
&lt;/h2&gt;

&lt;p&gt;A CLI that updates itself is convenient and scary. Vibrary's CLI checks for a new version at most once a day, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;downloads the npm tarball and checks its sha512 integrity,&lt;/li&gt;
&lt;li&gt;verifies an &lt;strong&gt;ed25519 signature&lt;/strong&gt; on the actual script with a public key baked into the old version,&lt;/li&gt;
&lt;li&gt;test-runs the new file,&lt;/li&gt;
&lt;li&gt;swaps it in and keeps the previous one around.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If any step fails, nothing changes. The signing key never leaves my machine, so a compromised npm account alone can't push code to people.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Realtime presence was the hardest part
&lt;/h2&gt;

&lt;p&gt;Presence (who's sitting where) runs on Supabase Realtime. The bug that took the longest: people sometimes showed up on the &lt;strong&gt;wrong floor&lt;/strong&gt; after reconnecting.&lt;/p&gt;

&lt;p&gt;The cause: one person can briefly have more than one presence entry (a reload, two tabs), and I was reading the first one. The fix was boring but solid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;always trust the &lt;strong&gt;newest&lt;/strong&gt; entry,&lt;/li&gt;
&lt;li&gt;re-reconcile the whole room every 15 seconds instead of only reacting to events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People who close the tab but whose AI is still working stay seated as "ghosts", and members who were here in the last 24 hours stay as "resting" (with a little zZ), so the room doesn't feel empty when you're the only one awake.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Make it fast by measuring first
&lt;/h2&gt;

&lt;p&gt;A canvas full of text and bubbles redrawn every frame got heavy. My first guesses about what was slow were wrong; profiling showed text measurement and redrawing static boards every frame. Caching &lt;code&gt;measureText&lt;/code&gt; results and pre-rendering the boards into offscreen canvases helped, and so did loading the leaderboard in parallel instead of after everything else.&lt;/p&gt;

&lt;p&gt;One more fun one: the engine used to seat ~200 sample characters and then clear them in live mode, which meant the header briefly said "223 online" before showing the real number. Now live mode simply never seats them.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Anti-cheat that punished the wrong person
&lt;/h2&gt;

&lt;p&gt;I added daily caps to stop people from gaming the leaderboard: over 30 deploys in a day and that day got flagged and dropped from rankings. The first person it caught was me, on a heavy deploy day.&lt;/p&gt;

&lt;p&gt;I removed automatic flagging. Everything counts now, unusually busy days are listed for manual review, and only a human can exclude a day. Automated "fairness" that hits your most active users is worse than none.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone starting a similar project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep the fun part (the look and feel) in a fast prototype as long as you can.&lt;/li&gt;
&lt;li&gt;Decide what you will &lt;strong&gt;never&lt;/strong&gt; collect on day one, and enforce it on the server, not just the client.&lt;/li&gt;
&lt;li&gt;Treat auto-update as a security feature, not a convenience.&lt;/li&gt;
&lt;li&gt;Profile before optimizing — especially when an AI is writing the optimizations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vibrary is free, and you can look around without signing in: &lt;strong&gt;&lt;a href="https://vibrary.dev/?ref=devto" rel="noopener noreferrer"&gt;vibrary.dev&lt;/a&gt;&lt;/strong&gt;. If you code with agents for hours, I'd love to hear what would make you keep it open in a tab.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
