<?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: Katsuo Imai</title>
    <description>The latest articles on DEV Community by Katsuo Imai (@katsuo-chang).</description>
    <link>https://dev.to/katsuo-chang</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3888044%2F08d22823-b875-4ed7-ad5d-ed3323a8611d.jpg</url>
      <title>DEV Community: Katsuo Imai</title>
      <link>https://dev.to/katsuo-chang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/katsuo-chang"/>
    <language>en</language>
    <item>
      <title>Building PROTO RECON — From Vague Idea to a Browser-Based Tactical HUD</title>
      <dc:creator>Katsuo Imai</dc:creator>
      <pubDate>Sun, 24 May 2026 00:12:32 +0000</pubDate>
      <link>https://dev.to/katsuo-chang/building-proto-recon-from-vague-idea-to-a-browser-based-tactical-hud-2p78</link>
      <guid>https://dev.to/katsuo-chang/building-proto-recon-from-vague-idea-to-a-browser-based-tactical-hud-2p78</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This post walks through how &lt;strong&gt;PROTO RECON&lt;/strong&gt; — an experimental app that combines phone sensors with in-browser ML — evolved from pre-coding requirements to its current implementation. I've verified the UI behavior, but I'm still at the stage of reading the source code for the first time. Here I organize the original requirements and an AI-generated source map as a foundation for deeper dives in future posts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo (experimental)&lt;/strong&gt;: &lt;a href="https://protorecon.devdevserver.workers.dev/" rel="noopener noreferrer"&gt;Proto Recon (Cloudflare Workers)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/katsutoshi0katsutoshi/proto_recon" rel="noopener noreferrer"&gt;github.com/katsutoshi0katsutoshi/proto_recon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sandbox for isolated experiments&lt;/strong&gt;: &lt;a href="https://codepen.io/wlzpaovm-the-bashful" rel="noopener noreferrer"&gt;CodePen / @wlzpaovm-the-bashful&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is still in testing. Performance tuning for heavy workloads hasn't been done yet — use at your own risk.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Where It Started
&lt;/h2&gt;

&lt;p&gt;It began with a vague question: &lt;em&gt;Can we build a digital user experience on top of the real world?&lt;/em&gt; The seed of the idea was whether today's phone sensors could overlay something like the robot-view or cockpit-view perspectives from old sci-fi and games onto live scenery.&lt;/p&gt;

&lt;p&gt;Before writing code, I summarized the requirements, turned functional specs into prose through dialogue with AI, and then started development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technology Choices (Before Implementation)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Adopted / Considered&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Object detection&lt;/td&gt;
&lt;td&gt;TensorFlow.js (COCO-SSD / BlazeFace)&lt;/td&gt;
&lt;td&gt;"Object recognition" as the core of the robot-view feel, fully in the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;Leaflet&lt;/td&gt;
&lt;td&gt;Familiar "you are here" display from games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Noise / effects&lt;/td&gt;
&lt;td&gt;Initially GLSL / three.js&lt;/td&gt;
&lt;td&gt;Wanted to reuse past effect-building experience&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The noise layer was added later. When I had Cursor implement it, &lt;strong&gt;three.js was skipped in favor of Canvas 2D and CSS&lt;/strong&gt; for CRT-style effects. Writing directly without extra modules kept things lighter — a decision that has paid off on real devices.&lt;/p&gt;

&lt;p&gt;Gyro, compass, altitude (on supported devices), GPS, and similar sensors were also in scope, wired to the HUD and minimap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Requirements at the Time (Summary)
&lt;/h2&gt;

&lt;p&gt;The planning document — built through repeated conversations with AI — was titled &lt;strong&gt;Tactical Recon &amp;amp; Guidance Terminal "PROJECT: LOCK-ON (working title)"&lt;/strong&gt;. At that point, sensor accuracy led us to assume &lt;strong&gt;Phase 2 would require a Unity (native) rewrite&lt;/strong&gt;. After coding with Cursor, however, things moved better than expected even without three.js, and the outlook shifted to &lt;strong&gt;Phase 1 might be achievable on the web alone&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Concept
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visuals&lt;/strong&gt;: Monochrome green military-terminal UI, scan lines, noise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI&lt;/strong&gt;: Real-time object detection for targeting and lock-on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immersion&lt;/strong&gt;: Gyro, altitude, BEEP sounds — instrument and audio feedback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future&lt;/strong&gt;: VR-goggle HUD, gaze control (concept stage)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Plan vs. Current State
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Plan (as of Apr 2026)&lt;/th&gt;
&lt;th&gt;Current (PROTO RECON)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stack&lt;/td&gt;
&lt;td&gt;Next.js + Three.js + TF.js&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Vite + vanilla JS&lt;/strong&gt; + TF.js (no Three.js)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Altimeter&lt;/td&gt;
&lt;td&gt;Barometric sensor (native assumed)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Geolocation &lt;code&gt;altitude&lt;/code&gt;&lt;/strong&gt; (supported devices only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Map&lt;/td&gt;
&lt;td&gt;(lightly specified in the plan)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Leaflet&lt;/strong&gt; minimap implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missile effect&lt;/td&gt;
&lt;td&gt;Tap to fire a virtual missile&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Not implemented&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 2 Unity&lt;/td&gt;
&lt;td&gt;Barometric altitude, haptics, etc.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not started&lt;/strong&gt; (web prototype continues)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distance display&lt;/td&gt;
&lt;td&gt;(mostly undefined in planning)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;WebXR hit-test measurements only&lt;/strong&gt; (no bbox-estimated distance)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The full planning document is collapsed below due to length.&lt;/p&gt;

&lt;p&gt;
  Full planning document (PROJECT: LOCK-ON draft)
  &lt;p&gt;&lt;strong&gt;Tactical Recon &amp;amp; Guidance Terminal App — "PROJECT: LOCK-ON (working title)" Planning Document&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This document summarizes a concept for a smartphone camera-filter app that blends retro-futuristic UI with modern AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Project Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A "play" app that recreates the experience of a &lt;strong&gt;military recon terminal&lt;/strong&gt; from 1980s–90s sci-fi films and games, using live smartphone camera feed and AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Core Concept&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visuals&lt;/strong&gt;: Monochrome green evoking old terminals. Retro feel via scan lines and noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI interaction&lt;/strong&gt;: Automatic targeting and lock-on through real-time object detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immersion&lt;/strong&gt;: Gyro, barometric sensor (altimeter), BEEP feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt;: HUD and gaze control with VR goggles (e.g. Hacosco) — concept stage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Main Features (excerpt)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;3.1 Video filter&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Green overlay (night-vision goggle texture)&lt;/li&gt;
&lt;li&gt;Edge bar graphs and scan indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;3.2 AI lock-on&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic moving-object detection with green bounding-box tracking&lt;/li&gt;
&lt;li&gt;SE accelerates when the target stays in the reticle center&lt;/li&gt;
&lt;li&gt;Lock complete: box turns red, warning sound&lt;/li&gt;
&lt;li&gt;Tap to fire a virtual missile (effect)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;3.3 Sensor-linked instruments&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gyro: pitch and roll numeric display&lt;/li&gt;
&lt;li&gt;Altimeter: relative altitude from barometric sensor (iPhone, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Development Phases (roadmap at the time)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Phase 1&lt;/strong&gt;: Web prototype (Next.js + Three.js + TF.js). Shareable via URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 2&lt;/strong&gt;: Unity port. Barometric altitude, vibration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phase 3&lt;/strong&gt;: VR/XR. Stereoscopic display, gaze lock-on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Distribution Strategy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dev logs on Qiita / Zenn, short-form video on social, posts on Reddit, etc.&lt;/p&gt;

&lt;p&gt;Created: April 29, 2026 (planning stage)&lt;/p&gt;



&lt;/p&gt;




&lt;h2&gt;
  
  
  How Development Proceeded
&lt;/h2&gt;

&lt;p&gt;Coding was delegated to &lt;strong&gt;Cursor&lt;/strong&gt;, adding features one prompt at a time until the current state. For prompts, I load the requirements document into Gemini and ask what kind of prompt would work best. I've checked JS behavior through the UI, but reading the source comes next. The file list and dependency diagram below were AI-generated and cleaned up for this article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Source Layout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTML (root level)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main screen. BIOS/splash, boot progress, camera feed, reticle/detection overlay, compass/altitude tape, minimap, CRT effects, etc. Loads &lt;code&gt;/src/main.js&lt;/code&gt; as a module&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;privacy.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Privacy policy (static page: collected data, purpose of use, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oss-notice.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Static OSS / third-party license notice&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/&lt;/code&gt; — runtime files
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Entry, config, UI
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;App hub. Boot sequence, camera acquisition, start/stop of modules, HUD updates, user actions (start/stop, lock, .etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appConfig.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dev tuning defaults (camera, CRT, object detection, face mask, minimap, motion linkage, live health, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;applyDevConfig.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applies &lt;code&gt;devConfig.crt&lt;/code&gt; as CSS variables on &lt;code&gt;document.documentElement&lt;/code&gt; (vignette, scan lines, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uiLexicon.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single source for UI copy (splash, status, errors, minimap labels, etc.) including functions to apply text to HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;style.css&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Global layout, HUD, CRT, minimap, reticle styles (green military UI)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Video &amp;amp; AI inference
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tfBackend.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One-time TensorFlow.js backend init (WebGPU → WebGL → CPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;COCO-SSD object detection. Bounding-box drawing, lock zone, target events, inference health notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;facePrivacy.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;BlazeFace face detection and masking (privacy overlay)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lockTrackKeys.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stable IDs for targets in the lock zone (position quantization + match with previous frame)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Distance &amp;amp; AR
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;objectRange.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Distance to target on HUD — measured distances only (WebXR, etc.); no bbox-estimated distance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;webxrHitTestRange.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WebXR immersive-ar + hit-test for real-world distance (m) fed into &lt;code&gt;objectRange&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Navigation, map, sensors
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;compass.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Device heading (compass) wrapper. Heading parse, cardinal labels, iOS permission request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;miniMap.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Geolocation watch and minimap facade (position, heading, speed, altitude callbacks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;miniMapLeaflet.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Leaflet map init, current-location marker (heading arrow), tile display&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;motionHud.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tilt β/γ from &lt;code&gt;DeviceOrientation&lt;/code&gt; for the right vertical tape HUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;motionStabilityLink.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Links tilt/shake to reticle/minimap parallax and notifications (including vibration)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Feedback &amp;amp; monitoring
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;liveHealth.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lightweight live health monitoring (inference latency, consecutive failures, video stall) and HUD notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audioFx.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Web Audio API for boot, detection, lock-on, error sounds, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;src/&lt;/code&gt; — tests only (not used on screen)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lockTrackKeys.test.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unit tests for &lt;code&gt;lockTrackKeys.js&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;liveHealth.test.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unit tests for &lt;code&gt;liveHealth.js&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tfBackend.test.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unit tests for &lt;code&gt;tfBackend.js&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Dependencies (overview)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
  index[index.html] --&amp;gt; main[main.js]
  main --&amp;gt; config[appConfig / applyDevConfig / uiLexicon]
  main --&amp;gt; cam[camera + HUD]
  main --&amp;gt; det[personDetection]
  main --&amp;gt; face[facePrivacy]
  det --&amp;gt; tf[tfBackend]
  face --&amp;gt; tf
  main --&amp;gt; map[miniMap]
  map --&amp;gt; leaf[miniMapLeaflet]
  main --&amp;gt; nav[compass / motionHud / motionStabilityLink]
  main --&amp;gt; range[objectRange]
  range --&amp;gt; xr[webxrHitTestRange]
  main --&amp;gt; health[liveHealth]
  main --&amp;gt; sfx[audioFx]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;main.js&lt;/code&gt; ties almost everything together; &lt;code&gt;appConfig.js&lt;/code&gt; settings and &lt;code&gt;uiLexicon.js&lt;/code&gt; copy drive behavior and display across modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing — What to Read Next
&lt;/h2&gt;

&lt;p&gt;Future posts will open the source and go deep feature by feature. The study roadmap below is the planned reading order (&lt;strong&gt;01&lt;/strong&gt; is done with this article's file map).&lt;/p&gt;

&lt;p&gt;
  Source reading roadmap (32 parts — click to expand)
  &lt;h3&gt;
  
  
  Stage 1 — Config and skeleton (no permissions needed)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;Core tech&lt;/th&gt;
&lt;th&gt;Common pitfalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;del&gt;&lt;strong&gt;01&lt;/strong&gt;&lt;/del&gt;&lt;/td&gt;
&lt;td&gt;&lt;del&gt;Full map — roles in &lt;code&gt;src/&lt;/code&gt;&lt;/del&gt;&lt;/td&gt;
&lt;td&gt;&lt;del&gt;&lt;code&gt;-&lt;/code&gt;&lt;/del&gt;&lt;/td&gt;
&lt;td&gt;&lt;del&gt;Which file owns what&lt;/del&gt;&lt;/td&gt;
&lt;td&gt;&lt;del&gt;Memorizing filenames without reading code&lt;/del&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;02&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;devConfig&lt;/code&gt; — behavior remote control&lt;/td&gt;
&lt;td&gt;&lt;code&gt;appConfig.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Camera, detection, CRT key meanings&lt;/td&gt;
&lt;td&gt;One change affects the whole app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;03&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single source for UI copy — &lt;code&gt;uiLexicon&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;uiLexicon.js&lt;/code&gt;, &lt;code&gt;index.html&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Centralized strings and error messages&lt;/td&gt;
&lt;td&gt;Finding leftover hardcoded text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;04&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CRT tokens to CSS — &lt;code&gt;applyDevConfig&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;applyDevConfig.js&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JS config → CSS variables (&lt;code&gt;:root&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Typo in variable name breaks styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;05&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTML skeleton — splash &amp;amp; HUD DOM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Layer stack (video / canvas / HUD)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;z-index&lt;/code&gt; vs &lt;code&gt;hidden&lt;/code&gt; attribute&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Stage 2 ⭐⭐ — Look and boot "theater" (testable without camera)
&lt;/h3&gt;

&lt;p&gt;UI animation and audio control without device permissions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;Core tech&lt;/th&gt;
&lt;th&gt;Common pitfalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;06&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Splash, BIOS, scramble text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main.js&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;First-run UI flow, timer control&lt;/td&gt;
&lt;td&gt;Duplicate timers, garbled text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;07&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Boot overlay — progress and steps&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Async status during startup&lt;/td&gt;
&lt;td&gt;Failed step never transitions to &lt;code&gt;error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;08&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web Audio — play after click&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audioFx.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AudioContext&lt;/code&gt; and user gesture&lt;/td&gt;
&lt;td&gt;Autoplay policy, silent bugs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;09&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reticle SVG and fixed layout&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Center-fixed CSS layout&lt;/td&gt;
&lt;td&gt;Why &lt;code&gt;motionStabilityLink&lt;/code&gt; was disabled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Live HUD frame and CRT noise&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;style.css&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Scan-line effects over video&lt;/td&gt;
&lt;td&gt;Rendering cost of noise &lt;code&gt;canvas&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Stage 3 ⭐⭐⭐ — Media, sensors, tapes (real device required)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;Core tech&lt;/th&gt;
&lt;th&gt;Common pitfalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Camera — &lt;code&gt;getUserMedia&lt;/code&gt; and rear preference&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;facingMode&lt;/code&gt;, stream acquisition&lt;/td&gt;
&lt;td&gt;iOS permission timing, connection hang&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Video presentation — &lt;code&gt;object-fit: cover&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;style.css&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hidden → visible preview transition&lt;/td&gt;
&lt;td&gt;Unintended camera zoom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full boot sequence — &lt;code&gt;startCamera&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After tap: camera → model load&lt;/td&gt;
&lt;td&gt;Failure logs when changing parallel vs sequential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heading tape — continuous angle and N jump&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;compass.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0°–360° unwrap, SVG control&lt;/td&gt;
&lt;td&gt;Clipping at north (N), insufficient padding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tilt tape — β / γ and &lt;code&gt;motionHud&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;motionHud.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;DeviceOrientation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deviceorientationabsolute&lt;/code&gt; behavior differences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;16&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Altitude tape — GPS altitude callback&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;miniMap.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Geolocation API&lt;/td&gt;
&lt;td&gt;Devices where altitude is &lt;code&gt;null&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimap — Leaflet and OSM/CARTO&lt;/td&gt;
&lt;td&gt;&lt;code&gt;miniMap.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;External tile map, marker&lt;/td&gt;
&lt;td&gt;Handling location denial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;18&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speed HUD — km/h from &lt;code&gt;coords.speed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;miniMap.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Speed from coordinate changes&lt;/td&gt;
&lt;td&gt;Speed shows &lt;code&gt;--&lt;/code&gt; when stationary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Stage 4 ⭐⭐⭐⭐ — Detection, privacy, ML
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;Core tech&lt;/th&gt;
&lt;th&gt;Common pitfalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;19&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TF.js backend — WebGPU→WebGL→CPU&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tfBackend.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Graphics API fallback&lt;/td&gt;
&lt;td&gt;Unsupported devices, first compile, heat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;COCO-SSD intro — &lt;code&gt;ensureModel&lt;/code&gt; and throttling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model load, inference throttling&lt;/td&gt;
&lt;td&gt;CDN load failure, dynamic &lt;code&gt;import()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;21&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detection box drawing — &lt;code&gt;drawVideoCover&lt;/code&gt; and canvas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drawing aligned to &lt;code&gt;video&lt;/code&gt; aspect&lt;/td&gt;
&lt;td&gt;Coordinate drift, DPR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lock-on — zone, dwell, key stabilization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tracking ID, smoothing&lt;/td&gt;
&lt;td&gt;ID swaps, flicker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;23&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distance — WebXR hit-test only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;objectRange.js&lt;/code&gt;, &lt;code&gt;webxrHitTestRange.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Measured distance on HUD&lt;/td&gt;
&lt;td&gt;No distance on unsupported devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;24&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Face mosaic — BlazeFace and canvas compositing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;facePrivacy.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;rAF + throttled inference&lt;/td&gt;
&lt;td&gt;Draw cycle vs inference collision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mosaic interval and heat — &lt;code&gt;intervalMs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;facePrivacy.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inference FPS vs load tradeoff&lt;/td&gt;
&lt;td&gt;Device freeze, thermal throttling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Stage 5 ⭐⭐⭐⭐⭐ — Integration, ops, errors
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;Core tech&lt;/th&gt;
&lt;th&gt;Common pitfalls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;26&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Live health — latency, video stall&lt;/td&gt;
&lt;td&gt;&lt;code&gt;liveHealth.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main-thread stall monitoring&lt;/td&gt;
&lt;td&gt;False positives under load spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;27&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Startup error handling — timeout and &lt;code&gt;catch&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Async error gaps&lt;/td&gt;
&lt;td&gt;Model load timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;28&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Second visit and cache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vite.config.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PWA, Vite build&lt;/td&gt;
&lt;td&gt;&lt;code&gt;failed to fetch dynamically imported module&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;29&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Revisit and restart — model disposal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Memory leak prevention&lt;/td&gt;
&lt;td&gt;Second run extremely heavy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;motionStabilityLink&lt;/code&gt; — why parallax was cut&lt;/td&gt;
&lt;td&gt;&lt;code&gt;motionStabilityLink.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parallax vs readability&lt;/td&gt;
&lt;td&gt;Janky rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integration — &lt;code&gt;main.js&lt;/code&gt; orchestration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main.js&lt;/code&gt; (full)&lt;/td&gt;
&lt;td&gt;Lifecycle management&lt;/td&gt;
&lt;td&gt;Tight coupling breaks on touch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;32&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrospective — bug list and what's next&lt;/td&gt;
&lt;td&gt;App overall&lt;/td&gt;
&lt;td&gt;Settling on load mitigation&lt;/td&gt;
&lt;td&gt;Defining "fatal bugs are gone"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;/p&gt;

&lt;p&gt;The next post will start with roadmap &lt;strong&gt;#02&lt;/strong&gt; (&lt;code&gt;appConfig.js&lt;/code&gt;).&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tensorflow</category>
      <category>vite</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Episode 0: Deconstructing a Vibe-Coded Web App — 32 Steps From Black Box to Mine</title>
      <dc:creator>Katsuo Imai</dc:creator>
      <pubDate>Fri, 22 May 2026 12:06:24 +0000</pubDate>
      <link>https://dev.to/katsuo-chang/episode-0-deconstructing-a-vibe-coded-web-app-32-steps-from-black-box-to-mine-4ip7</link>
      <guid>https://dev.to/katsuo-chang/episode-0-deconstructing-a-vibe-coded-web-app-32-steps-from-black-box-to-mine-4ip7</guid>
      <description>&lt;h1&gt;
  
  
  Episode 0: Turning a “Vibe-Coded” Black Box Into My Own App — A 32-Step Deconstruction Roadmap
&lt;/h1&gt;

&lt;p&gt;I built an advanced web app in a burst of &lt;strong&gt;vibe coding&lt;/strong&gt;—leaning hard on AI (Cursor and the like)—and packed in face recognition, WebGPU, and mobile sensors (heading, tilt, GPS).&lt;/p&gt;

&lt;p&gt;Near the finish line, load-related bugs showed up. Then it hit me: &lt;strong&gt;I don’t understand what the code is doing, or why the bugs exist. Is this really my app?&lt;/strong&gt; That disconnect—and a kind of guilt—was strong.&lt;/p&gt;

&lt;p&gt;So I’m treating this black-box app as the best textbook I have: &lt;strong&gt;break it apart, learn the mechanics from the ground up, and turn it into real skill.&lt;/strong&gt; This post is the index for that deconstruction journal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live deploy (for verification):&lt;/strong&gt; &lt;a href="https://protorecon.devdevserver.workers.dev/" rel="noopener noreferrer"&gt;Proto Recon (Cloudflare Workers)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/katsutoshi0katsutoshi/proto_recon" rel="noopener noreferrer"&gt;github.com/katsutoshi0katsutoshi/proto_recon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extracted experiments:&lt;/strong&gt; &lt;a href="https://codepen.io/wlzpaovm-the-bashful" rel="noopener noreferrer"&gt;CodePen / @wlzpaovm-the-bashful&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧭 Deconstruction roadmap (5 phases / 32 steps)
&lt;/h2&gt;

&lt;p&gt;To avoid burning out, the order starts with low-dependency pieces—&lt;strong&gt;config and UI polish&lt;/strong&gt;—and ramps up toward &lt;strong&gt;hardware control&lt;/strong&gt; and &lt;strong&gt;ML / performance&lt;/strong&gt;. Anything that can stand alone gets a minimal CodePen (or similar) as we go. (This plan was also AI-assisted.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1 ⭐ — Map &amp;amp; settings (learn by touching)
&lt;/h3&gt;

&lt;p&gt;First: the big picture of the codebase, then how static config and copy steer the whole app.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;What you learn (core tech)&lt;/th&gt;
&lt;th&gt;Common errors &amp;amp; traps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;01&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Big picture — roles under &lt;code&gt;src/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which file owns what&lt;/td&gt;
&lt;td&gt;Memorizing filenames without reading bodies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;02&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;devConfig&lt;/code&gt; — remote control for behavior&lt;/td&gt;
&lt;td&gt;&lt;code&gt;appConfig.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keys for camera, detection, CRT&lt;/td&gt;
&lt;td&gt;One change ripples everywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;03&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single source for UI copy: &lt;code&gt;uiLexicon&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;uiLexicon.js&lt;/code&gt;, &lt;code&gt;index.html&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Centralized strings &amp;amp; errors&lt;/td&gt;
&lt;td&gt;Finding leftover hard-coded text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;04&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CRT tokens into CSS — &lt;code&gt;applyDevConfig&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;applyDevConfig.js&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JS config → CSS variables on &lt;code&gt;:root&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Typo in var name → styles silently fail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;05&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTML skeleton — splash &amp;amp; HUD DOM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Layers (video / canvas / HUD)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;z-index&lt;/code&gt; vs the &lt;code&gt;hidden&lt;/code&gt; attribute&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Phase 2 ⭐⭐ — Look &amp;amp; boot “theater” (no camera permission needed)
&lt;/h3&gt;

&lt;p&gt;UI animation and audio you can verify without device permissions—pure front-end.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;What you learn (core tech)&lt;/th&gt;
&lt;th&gt;Common errors &amp;amp; traps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;06&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Splash, BIOS, scramble text&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main.js&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;First-run UI flow &amp;amp; timers&lt;/td&gt;
&lt;td&gt;Double-started timers, garbled text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;07&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Boot overlay — progress &amp;amp; steps&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Async status during startup&lt;/td&gt;
&lt;td&gt;Failed steps never move to &lt;code&gt;error&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;08&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web Audio — play after click&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audioFx.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AudioContext&lt;/code&gt; &amp;amp; user gesture&lt;/td&gt;
&lt;td&gt;Autoplay policy, silent output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;09&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reticle SVG &amp;amp; fixed layout&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;index.html&lt;/code&gt;, &lt;code&gt;style.css&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Center-fixed CSS layout&lt;/td&gt;
&lt;td&gt;Why &lt;code&gt;motionStabilityLink&lt;/code&gt; was cut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Live HUD frame &amp;amp; CRT noise&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;style.css&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Scanline overlay on video&lt;/td&gt;
&lt;td&gt;Noise &lt;code&gt;canvas&lt;/code&gt; render cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Phase 3 ⭐⭐⭐ — Media, sensors &amp;amp; tapes (real device required)
&lt;/h3&gt;

&lt;p&gt;Core Web APIs: camera, gyro, GPS.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;What you learn (core tech)&lt;/th&gt;
&lt;th&gt;Common errors &amp;amp; traps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Camera — &lt;code&gt;getUserMedia&lt;/code&gt; &amp;amp; rear preference&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;facingMode&lt;/code&gt;, stream acquisition&lt;/td&gt;
&lt;td&gt;iOS permission timing, hung connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How video is shown — &lt;code&gt;object-fit: cover&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;style.css&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hidden → visible preview&lt;/td&gt;
&lt;td&gt;Unintended “zoom” on camera feed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full boot sequence — &lt;code&gt;startCamera&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tap → camera → model load&lt;/td&gt;
&lt;td&gt;Failures when parallel vs sequential order changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;14&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heading tape — continuous angle &amp;amp; N jump&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;compass.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0°–360° unwrap, SVG control&lt;/td&gt;
&lt;td&gt;Clipping at north (N), insufficient padding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;15&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tilt tape — β / γ &amp;amp; &lt;code&gt;motionHud&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;motionHud.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Device tilt (&lt;code&gt;DeviceOrientation&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deviceorientationabsolute&lt;/code&gt; differences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;16&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Altitude tape — GPS altitude callback&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;miniMap.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Geolocation position &amp;amp; altitude&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;altitude&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; on some devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mini map — Leaflet &amp;amp; OSM/CARTO&lt;/td&gt;
&lt;td&gt;&lt;code&gt;miniMap.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;External tiles, markers&lt;/td&gt;
&lt;td&gt;Handling denied location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;18&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speed HUD — &lt;code&gt;coords.speed&lt;/code&gt; to km/h&lt;/td&gt;
&lt;td&gt;&lt;code&gt;miniMap.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Speed from geo updates&lt;/td&gt;
&lt;td&gt;Speed shows &lt;code&gt;--&lt;/code&gt; while stationary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Phase 4 ⭐⭐⭐⭐ — Detection, privacy &amp;amp; ML backend (hardest)
&lt;/h3&gt;

&lt;p&gt;The app’s main bottleneck—and the exciting part: &lt;strong&gt;real-time ML in the browser (TensorFlow.js)&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;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;What you learn (core tech)&lt;/th&gt;
&lt;th&gt;Common errors &amp;amp; traps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;19&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TF.js backend — WebGPU → WebGL → CPU&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tfBackend.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Graphics API fallback chain&lt;/td&gt;
&lt;td&gt;Unsupported devices, first compile, heat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;COCO-SSD intro — &lt;code&gt;ensureModel&lt;/code&gt; &amp;amp; throttling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Model load, inference throttling&lt;/td&gt;
&lt;td&gt;CDN load failure, dynamic &lt;code&gt;import()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;21&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detection boxes — &lt;code&gt;drawVideoCover&lt;/code&gt; &amp;amp; canvas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Canvas aligned to &lt;code&gt;video&lt;/code&gt; aspect&lt;/td&gt;
&lt;td&gt;Coordinate drift, device pixel ratio (DPR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lock-on — zones, dwell, key stabilization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;personDetection.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Track ID retention, smoothing&lt;/td&gt;
&lt;td&gt;ID swapping, flicker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;23&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distance display — bbox estimate &amp;amp; WebXR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;objectRange.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pseudo-range from bounding box&lt;/td&gt;
&lt;td&gt;Fallback when WebXR unavailable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;24&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Face mosaic — BlazeFace &amp;amp; canvas composite&lt;/td&gt;
&lt;td&gt;&lt;code&gt;facePrivacy.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;requestAnimationFrame&lt;/code&gt; + throttled inference&lt;/td&gt;
&lt;td&gt;Camera draw vs inference cycle collision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mosaic interval &amp;amp; heat — tuning &lt;code&gt;intervalMs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;facePrivacy.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inference FPS vs device load&lt;/td&gt;
&lt;td&gt;Freeze / thermal runaway when FPS too high&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Phase 5 ⭐⭐⭐⭐⭐ — Integration, ops &amp;amp; errors (toward production quality)
&lt;/h3&gt;

&lt;p&gt;Bugs that appear when parts are wired together—including the ones I’m hitting now—and finishing the app as a whole.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Title / theme&lt;/th&gt;
&lt;th&gt;Main files&lt;/th&gt;
&lt;th&gt;What you learn (core tech)&lt;/th&gt;
&lt;th&gt;Common errors &amp;amp; traps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;26&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Live health — stall &amp;amp; frozen video detection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;liveHealth.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main-thread stall monitoring&lt;/td&gt;
&lt;td&gt;False positives under load, retry loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;27&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Startup errors — timeout &amp;amp; &lt;code&gt;catch&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Orphan async work, stream cleanup&lt;/td&gt;
&lt;td&gt;Timeouts on huge model loads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;28&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Second visit &amp;amp; cache — asset deploy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vite.config.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PWA cache, Vite build&lt;/td&gt;
&lt;td&gt;&lt;code&gt;failed to fetch dynamically imported module&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;29&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Revisit &amp;amp; restart — stop &amp;amp; dispose models&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Leak prevention, releasing instances&lt;/td&gt;
&lt;td&gt;Second launch much heavier than first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;30&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;motionStabilityLink&lt;/code&gt; — why parallax was cut&lt;/td&gt;
&lt;td&gt;&lt;code&gt;motionStabilityLink.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parallax vs readability&lt;/td&gt;
&lt;td&gt;Jank when performance drops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integration pass — &lt;code&gt;main.js&lt;/code&gt; orchestration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main.js&lt;/code&gt; (whole file)&lt;/td&gt;
&lt;td&gt;Module lifecycle&lt;/td&gt;
&lt;td&gt;“Touch anything, break everything” coupling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;32&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrospective — bugs fixed &amp;amp; what’s next&lt;/td&gt;
&lt;td&gt;App-wide&lt;/td&gt;
&lt;td&gt;Load fixes, skills that stuck&lt;/td&gt;
&lt;td&gt;Defining “fatal bugs are gone”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🛠️ How I’ll actually move forward
&lt;/h2&gt;

&lt;p&gt;Taken seriously, 32 steps at one post per day is roughly a month; realistically, one or two posts per week means &lt;strong&gt;six months or more&lt;/strong&gt;. It’s not an obligation—I’ll go at an easy pace, sitting down properly for each piece. I’ll still use AI to polish prose and to help draft posts.&lt;/p&gt;

&lt;p&gt;I’ve tasted the fun of AI-assisted idea generation. &lt;strong&gt;On purpose, I’m now dismantling this heavy mock&lt;/strong&gt;—using AI to push ideas to the max, then learning backward until I can rebuild them with my own hands.&lt;/p&gt;

&lt;p&gt;Minimal slices go into &lt;a href="https://codepen.io/wlzpaovm-the-bashful" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt; as a collection. Months from now, when this index is full of links and I can explain the bugs in my own words, the app should finally be &lt;strong&gt;mine in name and in fact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I’m already close to losing heart while writing this. I assume AI will upgrade fast enough that parts of this article feel dated. &lt;strong&gt;Episode 0 is here—will Episode 1 actually show up?&lt;/strong&gt; I don’t know yet. But that’s the bet.&lt;/p&gt;

</description>
      <category>learninpublic</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tensorflow</category>
    </item>
    <item>
      <title>What I Learned Shipping TightTimeLog: AI Workflow, CSP, AdSense, and Distribution</title>
      <dc:creator>Katsuo Imai</dc:creator>
      <pubDate>Mon, 20 Apr 2026 02:36:34 +0000</pubDate>
      <link>https://dev.to/katsuo-chang/what-i-learned-shipping-tighttimelog-ai-workflow-csp-adsense-and-distribution-318j</link>
      <guid>https://dev.to/katsuo-chang/what-i-learned-shipping-tighttimelog-ai-workflow-csp-adsense-and-distribution-318j</guid>
      <description>&lt;p&gt;I built and launched &lt;a href="https://tighttimelog.org/" rel="noopener noreferrer"&gt;TightTimeLog&lt;/a&gt;—a small offline-first PWA timer with bilingual UI and on-device logs—as a solo project. Along the way I hit &lt;strong&gt;AdSense rejection&lt;/strong&gt;, underwhelming distribution experiments, and plenty of CSP surprises. This post bundles several shorter notes I published on my &lt;a href="https://katsuo-chang.hatenablog.com/" rel="noopener noreferrer"&gt;Hatena blog&lt;/a&gt; into &lt;strong&gt;one English piece&lt;/strong&gt; for DEV.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;TightTimeLog was my first “ship it” personal project: PWA behavior, EN/JA UI, IndexedDB logs, and a focus on keeping data on the user’s device. &lt;strong&gt;Google AdSense did not approve the site&lt;/strong&gt; (at least on the attempts I made), and marketing pushes did not move the needle as much as I hoped. Still, finishing deployment and seeing &lt;em&gt;where&lt;/em&gt; things break was more valuable than the binary pass/fail. Below I split lessons into &lt;strong&gt;engineering&lt;/strong&gt;, &lt;strong&gt;AI-assisted workflow&lt;/strong&gt;, and &lt;strong&gt;distribution / monetization mindset&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Building with Cursor + Cloudflare Pages (and what hurt)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why this stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; conversational coding, refactors, and research in one loop—huge for a solo dev’s throughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Pages:&lt;/strong&gt; static deploy + HTTPS + CDN with minimal ops—great default for a hobby site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PWA and a little differentiation
&lt;/h3&gt;

&lt;p&gt;I added &lt;strong&gt;haptic feedback&lt;/strong&gt; on supported phones so the app feels slightly more “app-like” than a bare timer page. It’s a small touch, but it matches the goal of a pleasant mobile experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design upfront
&lt;/h3&gt;

&lt;p&gt;Using &lt;strong&gt;Figma early&lt;/strong&gt; for icons and layout reduced late rework. For side projects, investing a bit in visuals before features explode pays off.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Practical prompting for i18n and “legal-ish” drafts (Cursor / Gemini)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  i18n
&lt;/h3&gt;

&lt;p&gt;The trick was to pin down &lt;strong&gt;library assumptions, output shape, and file layout&lt;/strong&gt; in the prompt instead of saying “make it multilingual.” For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Externalize all user-visible strings; keep English and Japanese keys consistent with the existing &lt;code&gt;app.js&lt;/code&gt; i18n object pattern; don’t leave hard-coded copy in HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fewer surprises, fewer round trips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy policy drafts
&lt;/h3&gt;

&lt;p&gt;I listed facts only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where data lives (e.g., IndexedDB, not uploaded)&lt;/li&gt;
&lt;li&gt;Third parties involved (hosting, contact form, ads if any)&lt;/li&gt;
&lt;li&gt;How users can reach me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I edited the draft manually. &lt;strong&gt;AI reduces blank-page time; it doesn’t replace responsible review.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Cloudflare Pages + CSP: when “it worked locally” dies in production
&lt;/h2&gt;

&lt;p&gt;Production &lt;strong&gt;Content-Security-Policy&lt;/strong&gt; headers blocked third-party scripts and iframes I hadn’t allowlisted—especially after adding AdSense-related domains. Fixing it meant evolving &lt;code&gt;_headers&lt;/code&gt; until the live response headers matched what the browser needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mental model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;/*
  Content-Security-Policy: default-src 'self'; script-src 'self' https://pagead2.googlesyndication.com ... ; frame-src https://googleads.g.doubleclick.net ... ;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your exact directive will differ by which services you embed. I treated the browser console’s CSP violations as the source of truth for what to add.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checklist I wish I had on day one
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Inspect &lt;strong&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt; on the real origin&lt;/strong&gt; (not &lt;code&gt;file://&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Watch the console for &lt;strong&gt;CSP violations&lt;/strong&gt; after each new script or embed.&lt;/li&gt;
&lt;li&gt;After changing &lt;code&gt;_headers&lt;/code&gt;, &lt;strong&gt;re-deploy&lt;/strong&gt; and confirm headers changed (avoid stale SW caches during testing).&lt;/li&gt;
&lt;li&gt;When using a &lt;strong&gt;service worker&lt;/strong&gt;, bump cache versions or test in a clean profile if HTML responses look “stuck.”&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. AdSense rejection: my self-analysis (non-legal, non-official)
&lt;/h2&gt;

&lt;p&gt;Google doesn’t owe us a precise reason, but my working theory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product was optimized to be &lt;strong&gt;minimal UI copy&lt;/strong&gt;, which can look like &lt;strong&gt;thin content&lt;/strong&gt; to automated review.&lt;/li&gt;
&lt;li&gt;Policy also cares about &lt;strong&gt;where&lt;/strong&gt; ads appear—&lt;strong&gt;navigation-only or low-value shells&lt;/strong&gt; should not carry ads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I responded by adding &lt;strong&gt;original explanatory copy&lt;/strong&gt; on main screens and keeping ad tags off purely supplementary pages. Monetization-wise, I’m treating AdSense as &lt;strong&gt;one experiment&lt;/strong&gt;, not the only path.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Posting on Qiita, X, and Product Hunt: distribution lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rough outcomes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X:&lt;/strong&gt; posting cold from a fresh account barely moved traffic. I skipped &lt;strong&gt;community building&lt;/strong&gt; (hashtags, consistent presence) and paid for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qiita &amp;amp; Product Hunt:&lt;/strong&gt; dropping a link alone didn’t create sustained traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context beats bare links&lt;/strong&gt;—show up where your audience already discusses problems you solve.&lt;/li&gt;
&lt;li&gt;Even tiny reactions matter; they’re fuel when metrics are flat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, I want to lean into &lt;strong&gt;Build in Public&lt;/strong&gt;: share the process, not only the launch URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Shipping still beat not shipping. I’ll keep iterating on &lt;a href="https://tighttimelog.org/" rel="noopener noreferrer"&gt;TightTimeLog&lt;/a&gt; and experiment with other projects too.&lt;/p&gt;

&lt;p&gt;Japanese dev diary: &lt;a href="https://katsuo-chang.hatenablog.com/" rel="noopener noreferrer"&gt;Hatena Blog&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Personal learning log—not legal or financial advice. Verify policies and products for your own situation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>pwa</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
