[{"data":1,"prerenderedAt":210},["ShallowReactive",2],{"kaizen-en":3},{"id":4,"title":5,"body":6,"description":202,"extension":203,"meta":204,"navigation":205,"path":206,"seo":207,"stem":208,"__hash__":209},"kaizen_en\u002Fen\u002Fkaizen.md","How Kaizen is built",{"type":7,"value":8,"toc":188},"minimark",[9,14,18,21,25,28,31,35,51,54,58,61,64,68,76,79,83,90,93,97,100,104,107,117,121,124,128,131,135,158],[10,11,13],"h2",{"id":12},"where-it-came-from","Where it came from",[15,16,17],"p",{},"In 2022 I started helping run the Discord community of Cajun Koi Academy, the study channel of YouTubers Mike and Matty. A year later I built Cody, a bot for that server with a Pomodoro timer, study-time tracking and leaderboards. People clearly focused better when someone else was there, and that observation became Kaizen’s Focus Rooms.",[15,19,20],{},"When the community’s founders moved on in 2025, they handed it to me and I renamed it Kaizen. In September 2025 I started on the app, with a hackathon during my minor as the final push. The first Android alpha reached testers on 26 September, iPhone testers followed in November, and version 3.0 launched on Google Play on 2 January 2026. About 350 people signed up that first day.",[10,22,24],{"id":23},"one-codebase-four-platforms","One codebase, four platforms",[15,26,27],{},"Kaizen is a single Expo app: React Native 0.86, React 19 and TypeScript in strict mode. The same code runs natively on iOS and Android, and as an installable web app through React Native for Web. The Windows version wraps that web build in Tauri 2, a small Rust shell that adds a tray icon, native notifications and remembered window positions, packaged as an MSIX for the Microsoft Store.",[15,29,30],{},"Invite links to a room open the installed app on every platform: universal links on iOS, app links on Android and an App URI Handler on Windows. Everyone else lands in the web app.",[10,32,34],{"id":33},"focus-rooms-without-a-camera","Focus Rooms, without a camera",[15,36,37,38,50],{},"Many body-doubling tools put you on video. A 2024 survey of 220 mostly neurodivergent people notes that video calls “may activate people’s social anxiety”.",[39,40,41],"sup",{},[42,43,49],"a",{"href":44,"ariaDescribedBy":45,"dataFootnoteRef":47,"id":48},"#user-content-fn-eagle",[46],"footnote-label","","user-content-fnref-eagle","1"," Focus Rooms keep the presence and drop the camera: you see who is focusing, who is on a break and who stepped away, plus the tag each person works under.",[15,52,53],{},"Presence is polled every 15 seconds. There is no realtime channel. Each member’s countdown ticks on the device from an end time anchored to the server clock, so it stays smooth between polls without drifting, and a member whose heartbeat is older than 45 seconds shows as Away. Version 4.0 rewrote the rooms after people kept getting removed by accident: a phone in the background now keeps its seat for 15 minutes, up from 2.",[10,55,57],{"id":56},"app-blocking-written-natively","App blocking, written natively",[15,59,60],{},"Blocking distracting apps needs operating-system features that no existing library covered well. The candidates I found were a few months old with a single maintainer, asked for the permission to list every installed app, or were stuck on an old Expo version. So I wrote my own Expo module with two native halves.",[15,62,63],{},"On Android, a Kotlin engine reads which app is in front through usage access and draws a block screen over it. It never uses the Accessibility service or the permission to list every installed app. On iPhone it uses Apple’s Screen Time: you pick apps in Apple’s own picker and Kaizen only receives a sealed token, so it can’t see what you chose. Three app extensions keep the shield in step across four processes. On both platforms the list of blocked apps stays on the phone.",[10,65,67],{"id":66},"a-mistake-version-4100","A mistake: version 4.10.0",[15,69,70,71,75],{},"App blocking shipped in 4.10.0 on 17 September 2026, and on Android it blocked nothing. Release builds are shrunk with R8, and without an ",[72,73,74],"code",{},"@OptimizedRecord"," annotation, Expo could no longer convert the settings passed to the native module. Every debug build worked. Only the release build failed, and it failed silently.",[15,77,78],{},"The fix, 4.10.1, was ready the same day. It also added an R8 keep rule as a safety net and a log line that flags an empty configuration. Google Play took about 24 hours to approve it, so for a full day, the feature Android users had just updated for didn’t work. Expo has since fixed the underlying cause in expo-modules-core 58.",[10,80,82],{"id":81},"sync-that-works-offline","Sync that works offline",[15,84,85,86,89],{},"Every change lands on the device first and syncs to Supabase in the background. Items get their IDs on the device, the newest ",[72,87,88],{},"updated_at"," wins a conflict, and deletions are recorded as tombstones, so another device can’t bring a deleted task back to life. Between full syncs, a device only pulls what changed since its last checkpoint.",[15,91,92],{},"Habit check-ins use an ID derived from the habit and the day. Two phones that check off the same habit while offline end up with one record instead of two.",[10,94,96],{"id":95},"testing","Testing",[15,98,99],{},"The app has hundreds of test files. Pure logic, such as the sync merge and the statistics, gets property-based tests with fast-check on top of regular examples. Stryker then changes that code in small ways and checks that the tests notice. CI fails when that mutation score drops below 80 percent. Typechecking, linting, formatting, the test suite and a check of every npm package signature run on every push to the main branches.",[10,101,103],{"id":102},"around-the-app","Around the app",[15,105,106],{},"Next to the app runs an admin panel: a Vue 3 app with TanStack Query that shows sign-ups, retention cohorts and a marker for every feature release on the charts. New timer backgrounds are converted to WebP in the browser with WebAssembly, and video loops are checked with WebCodecs before upload.",[15,108,109,110,116],{},"The store listings for three stores and seven languages live in their own repository, with scripts that check every field limit and that each translation has the same structure as the English original. The website, ",[42,111,115],{"href":112,"rel":113},"https:\u002F\u002Fmy-kaizen.com",[114],"nofollow","my-kaizen.com",", is built with Nuxt 4 and has long guides with sources. And the Discord server, with about 30,000 members, has forums for bug reports and feature requests.",[10,118,120],{"id":119},"what-the-numbers-say","What the numbers say",[15,122,123],{},"September 2026 was Kaizen’s biggest month so far:",[125,126],"stat-row",{":items":127},"[{\"value\":\"1,500\",\"label\":\"new sign-ups, a quarter of all time\"},{\"value\":\"750\",\"label\":\"people active that month, about 350 a week\"},{\"value\":\"6,200\",\"label\":\"hours of focus in that month alone\"}]",[15,129,130],{},"Two decisions stand out. Making tasks and joining public rooms free in March roughly doubled weekly sign-ups. Releasing in seven languages in April then lifted the share of new users who actually start using the app from about 23 to about 43 percent that spring. From spring to September, the number of new users who start each week roughly tripled. The next challenge is the second week: about one in seven new users comes back.",[10,132,134],{"id":133},"what-id-do-differently","What I’d do differently",[136,137,138,146,152],"ul",{},[139,140,141,145],"li",{},[142,143,144],"strong",{},"Translate from the start."," Activation jumped when seven languages arrived, three months after launch.",[139,147,148,151],{},[142,149,150],{},"Use a server-state library earlier."," The reads in the sync layer are hand-rolled, and TanStack Query is the planned replacement.",[139,153,154,157],{},[142,155,156],{},"Test the release build on a real phone before it ships."," In 4.10.0 every debug build worked; only the release build was broken.",[159,160,163,168],"section",{"className":161,"dataFootnotes":47},[162],"footnotes",[10,164,167],{"className":165,"id":46},[166],"sr-only","Footnotes",[169,170,171],"ol",{},[139,172,174,175,180,181],{"id":173},"user-content-fn-eagle","Eagle, T., Baltaxe-Admony, L. B. and Ringland, K. E. (2024). “It Was Something I Naturally Found Worked and Heard About Later”: An Investigation of Body Doubling with Neurodivergent Participants. ACM Transactions on Accessible Computing, 17(3). ",[42,176,179],{"href":177,"rel":178},"https:\u002F\u002Fdoi.org\u002F10.1145\u002F3689648",[114],"doi.org\u002F10.1145\u002F3689648"," ",[42,182,187],{"href":183,"ariaLabel":184,"className":185,"dataFootnoteBackref":47},"#user-content-fnref-eagle","Back to reference 1",[186],"data-footnote-backref","↩",{"title":47,"searchDepth":189,"depth":189,"links":190},2,[191,192,193,194,195,196,197,198,199,200,201],{"id":12,"depth":189,"text":13},{"id":23,"depth":189,"text":24},{"id":33,"depth":189,"text":34},{"id":56,"depth":189,"text":57},{"id":66,"depth":189,"text":67},{"id":81,"depth":189,"text":82},{"id":95,"depth":189,"text":96},{"id":102,"depth":189,"text":103},{"id":119,"depth":189,"text":120},{"id":133,"depth":189,"text":134},{"id":46,"depth":189,"text":167},"Kaizen is a Pomodoro timer with camera-free Focus Rooms on iOS, Android, the web and Windows, built and run by one developer. How it is made, including a release that went wrong.","md",{},true,"\u002Fen\u002Fkaizen",{"title":5,"description":202},"en\u002Fkaizen","0_YpRc1kOjBdUoFYgGufT2awa0iFCFTRCt3T0xHqYOc",1791315487489]