<?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: Ichimura Tomoo</title>
    <description>The latest articles on DEV Community by Ichimura Tomoo (@tomopumipumi).</description>
    <link>https://dev.to/tomopumipumi</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%2F4075513%2F8f38d7ca-12df-4729-b15a-096ddeb2301e.jpg</url>
      <title>DEV Community: Ichimura Tomoo</title>
      <link>https://dev.to/tomopumipumi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomopumipumi"/>
    <language>en</language>
    <item>
      <title>Bringing Modern UI Architecture to Garmin Apps: The Sweet Spot Between Performance and Testability</title>
      <dc:creator>Ichimura Tomoo</dc:creator>
      <pubDate>Fri, 04 Sep 2026 07:41:07 +0000</pubDate>
      <link>https://dev.to/tomopumipumi/bringing-modern-ui-architecture-to-garmin-apps-the-sweet-spot-between-performance-and-testability-4bi5</link>
      <guid>https://dev.to/tomopumipumi/bringing-modern-ui-architecture-to-garmin-apps-the-sweet-spot-between-performance-and-testability-4bi5</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Attempted to introduce modern UI architecture (unidirectional data flow and component separation) to Garmin app development, an environment severely constrained by kilobytes of RAM and a weak CPU.&lt;/li&gt;
&lt;li&gt;Built a custom Hooks-like library for the Monkey C environment, but calling it directly inside the render loop caused excessive CPU load.&lt;/li&gt;
&lt;li&gt;Adopted a "Props Packing Pattern" (passing arrays) and the "Container/Presenter Pattern" as a solution.&lt;/li&gt;
&lt;li&gt;Traded a negligible 0.8ms per-frame overhead for the ability to run headless UI integration tests and benchmarks without an emulator, unlocking a robust developer experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. The Challenge: Building a Rich UI in a Kilobyte World
&lt;/h2&gt;

&lt;p&gt;Developing for smartwatches, particularly Garmin devices, is a completely different beast compared to modern platforms like the Apple Watch or Wear OS. Even for a WatchApp (a standalone device app), memory allocation is measured in kilobytes, and while official specs aren't published, CPU clocks are estimated to be around a few hundred megahertz. These constraints are the necessary trade-offs that enable Garmin's greatest strength: battery life that lasts for weeks.&lt;/p&gt;

&lt;p&gt;I recently released &lt;strong&gt;YAMAKAGE&lt;/strong&gt;, an app that visualizes sunset and sunrise times while factoring in the actual surrounding terrain.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://apps.garmin.com/apps/48e48601-9506-4f67-b19c-59ca702c34b8" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;apps.garmin.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&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/tomopumipumi" rel="noopener noreferrer"&gt;
        tomopumipumi
      &lt;/a&gt; / &lt;a href="https://github.com/tomopumipumi/yamakage" rel="noopener noreferrer"&gt;
        yamakage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &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;YAMAKAGE (山影)&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;YAMAKAGE is an application system that combines topographical data around a user's current or specified location with the sun's trajectory to calculate and simulate the &lt;strong&gt;"true sunset and sunrise times"&lt;/strong&gt; hidden behind mountains or surrounding obstacles.&lt;/p&gt;
&lt;p&gt;During mountain climbing and outdoor activities, it accurately predicts the "actual darkness" that arrives earlier than the standard sunset time, supporting safer activity planning.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📦 Repository Structure (Monorepo)&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This repository is structured as a monorepo containing the backend API, the Web app frontend, and Garmin smartwatch apps (Data Field version and Standalone Watch App version).&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;
├── backend/                # Backend API (BFF) &amp;amp; Core Calculation Engine
│   ├── yamakage/           # 🌐 Cloudflare Workers API (TypeScript / Hono)
│   └── yamakage-wasm/      # ⚙️ Core Calculation Engine (Rust / WebAssembly)
├── web-site/               # 💻 Web Frontend (React / TypeScript / Vite)
├── yamakage-datafield/     # ⌚️ Garmin Connect IQ App (Data Field version / Monkey&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tomopumipumi/yamakage" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This app incorporates numerous intuitive and visual UI elements, including animations of the sun and moon's trajectories, a 360-degree panorama view, and radar charts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start Screen
&lt;/h3&gt;

&lt;p&gt;Tap the START button on the start screen to begin the calculation. Swipe up or down to switch between Sun Mode, Moon Mode, and the Settings screen in that order. It features an animation of the sun (and moon) spinning as it moves along an arc.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;【Sun Mode】&lt;/strong&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%2Fnixmelnr8ome6mmxaoq5.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%2Fnixmelnr8ome6mmxaoq5.png" alt=" " width="625" height="871"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Moon Mode】&lt;/strong&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%2Fwpizvllw9przrl55n4zd.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%2Fwpizvllw9przrl55n4zd.png" alt=" " width="625" height="873"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Settings Screen
&lt;/h3&gt;

&lt;p&gt;You can configure various settings. Currently, it only includes animation-related options.&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%2Fvfvwngckfjehw3d6q8pf.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%2Fvfvwngckfjehw3d6q8pf.png" alt=" " width="620" height="869"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Panorama View
&lt;/h3&gt;

&lt;p&gt;Displays an overlay of the elevation angle of obstacles (like mountains) in the direction the smartwatch is facing, along with the trajectory and current position of the sun (or moon).&lt;br&gt;
Features animations of floating clouds in Sun Mode and sparkling stars in Moon Mode.&lt;br&gt;
&lt;strong&gt;【Sun Mode】&lt;/strong&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%2F4zdpwap2oeqxxfqvh7ao.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%2F4zdpwap2oeqxxfqvh7ao.png" alt=" " width="625" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Moon Mode】&lt;/strong&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%2F8z3j0gbi9xuhda1qh3nq.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%2F8z3j0gbi9xuhda1qh3nq.png" alt=" " width="625" height="868"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Sky Map
&lt;/h3&gt;

&lt;p&gt;Provides a bird's-eye view of the 360-degree terrain elevation and the movement of the sun (or moon) across the sky in a circular graph.&lt;br&gt;
The perspective is exactly like looking up at the sky. The animations are similar to those in the Panorama View.&lt;br&gt;
&lt;strong&gt;【Sun Mode】&lt;/strong&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%2Fpgfwfxan307e7awu0txo.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%2Fpgfwfxan307e7awu0txo.png" alt=" " width="625" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Moon Mode】&lt;/strong&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%2F4zuekgcid8m7d0tvgo2k.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%2F4zuekgcid8m7d0tvgo2k.png" alt=" " width="625" height="871"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Radar View
&lt;/h3&gt;

&lt;p&gt;Provides a radar-like, top-down perspective to intuitively visualize the presence of surrounding obstacles and open directions. The distance (in km) to obstacles in the direction the watch is facing is also displayed in real-time.&lt;br&gt;
The perspective is like looking down at the ground from the sky.&lt;br&gt;
A sonar pulse-like wave animation enhances the atmosphere.&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%2Fr30yum04hx6z3rv43jx8.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%2Fr30yum04hx6z3rv43jx8.png" alt=" " width="625" height="868"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Details Screen
&lt;/h3&gt;

&lt;p&gt;You can check the exact "Sunrise (Moonrise)" and "Sunset (Moonset)" times taking the terrain into account, as well as the elevation angle of obstacles in the direction you are currently facing.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;【Sun Mode】&lt;/strong&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%2Fws9b9p236dzx7eackos6.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%2Fws9b9p236dzx7eackos6.png" alt="image.png" width="627" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;【Moon Mode】&lt;/strong&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%2F70zsoa9mm35f7z5afugk.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%2F70zsoa9mm35f7z5afugk.png" alt=" " width="623" height="867"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Loading Screen
&lt;/h3&gt;

&lt;p&gt;This screen is displayed from the time the START button is tapped until the calculation results are received from the backend.&lt;br&gt;
It features a spinning animation.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;【Sun Mode】&lt;/strong&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%2Fp8gfnt91pt30kribaz02.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%2Fp8gfnt91pt30kribaz02.png" alt="image.png" width="626" height="869"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;【Moon Mode】&lt;/strong&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%2Fx63lvofouyfgv8gu17k7.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%2Fx63lvofouyfgv8gu17k7.png" alt="image.png" width="627" height="869"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Error Screen
&lt;/h3&gt;

&lt;p&gt;It features a pulsing animation where the circular border around the exclamation mark expands and contracts.&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%2F30cfpp215imo7qmr4u2w.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%2F30cfpp215imo7qmr4u2w.png" alt="image.png" width="626" height="871"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To implement such complex UIs and state transitions in an extremely resource-constrained environment without the app crashing, a robust and scalable architecture was absolutely essential.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. The Ideal vs. The Trap: Custom "MonkeyHooks" and the Render Loop
&lt;/h2&gt;

&lt;p&gt;To prevent state management from becoming a tangled mess, I built a custom, lightweight state management library called &lt;strong&gt;"MonkeyHooks"&lt;/strong&gt; designed for Monkey C to achieve a React-like unidirectional data flow, and integrated it into the project.&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/tomopumipumi" rel="noopener noreferrer"&gt;
        tomopumipumi
      &lt;/a&gt; / &lt;a href="https://github.com/tomopumipumi/monkey-hooks" rel="noopener noreferrer"&gt;
        monkey-hooks
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &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;MonkeyHooks&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;MonkeyHooks is a state management and utility library for Garmin Connect IQ (Monkey C) application development.&lt;/p&gt;
&lt;p&gt;It is designed to improve maintainability by organizing processes such as UI state management, system resource sharing (timers and GPS), and screen transitions.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Use Case: YAMAKAGE&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The Garmin application "YAMAKAGE" is a practical example of MonkeyHooks in action.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/tomopumipumi/yamakage" rel="noopener noreferrer"&gt;YAMAKAGE(Repository)&lt;/a&gt;&lt;br&gt;
&lt;a href="https://apps.garmin.com/apps/48e48601-9506-4f67-b19c-59ca702c34b8?tid=2" rel="nofollow noopener noreferrer"&gt;YAMAKAGE(Connect IQ)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/173045582/642435387-8cda2c1b-71e9-45c9-a873-01fe399476ad.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg3LThjZGEyYzFiLTcxZTktNDVjOS1hODczLTAxZmUzOTk0NzZhZC5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wZmU4MDk1MzZmNWE4M2IyMTZhNTE2NTc1N2JhYThjYzU3ZmQzYTU0MDdmZDg2NmUyZDUxODA5NGM2NmFjNzU3JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.vtf7wldu5_X7kiE_qzNuvZ7n_btyBCIPkfSXGTUBgZg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F173045582%2F642435387-8cda2c1b-71e9-45c9-a873-01fe399476ad.jpg%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg3LThjZGEyYzFiLTcxZTktNDVjOS1hODczLTAxZmUzOTk0NzZhZC5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wZmU4MDk1MzZmNWE4M2IyMTZhNTE2NTc1N2JhYThjYzU3ZmQzYTU0MDdmZDg2NmUyZDUxODA5NGM2NmFjNzU3JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.vtf7wldu5_X7kiE_qzNuvZ7n_btyBCIPkfSXGTUBgZg" alt="HQaPT2Ka4AAXXRo.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/173045582/642435386-c363b3c0-c0e2-4ab2-af67-b42399e87cba.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg2LWMzNjNiM2MwLWMwZTItNGFiMi1hZjY3LWI0MjM5OWU4N2NiYS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iNWQxZjdkZjA3MDlhMjgxZGYwMjNhZTQ0NTE2NmMwZTkzMzM2MTU3ZWEwY2MwYTkzYzE0YTQ0N2JjYTQwMTQxJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.AEk4gb_CxSncVnPuXrvzZMecLK7Jw1E46quLfSFjkTg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F173045582%2F642435386-c363b3c0-c0e2-4ab2-af67-b42399e87cba.jpg%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg2LWMzNjNiM2MwLWMwZTItNGFiMi1hZjY3LWI0MjM5OWU4N2NiYS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iNWQxZjdkZjA3MDlhMjgxZGYwMjNhZTQ0NTE2NmMwZTkzMzM2MTU3ZWEwY2MwYTkzYzE0YTQ0N2JjYTQwMTQxJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.AEk4gb_CxSncVnPuXrvzZMecLK7Jw1E46quLfSFjkTg" alt="HQaPT2ebIAEco02.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/173045582/642435388-3b663b69-eb33-4a11-a9c9-3e8a8cf78543.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg4LTNiNjYzYjY5LWViMzMtNGExMS1hOWM5LTNlOGE4Y2Y3ODU0My5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kMDg0YTc4NDUzYzg3NTEzYjZmY2Q2ZTI0MDk4NDc3NmYyMjExMTljZWYxMjE4MmU5YzYyZjIwZTZmZmE0OThjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.W8PKJeKZVKnfs6t7CWUjQ3Vz_EUeVE3joaqQE48yutU"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F173045582%2F642435388-3b663b69-eb33-4a11-a9c9-3e8a8cf78543.jpg%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg4LTNiNjYzYjY5LWViMzMtNGExMS1hOWM5LTNlOGE4Y2Y3ODU0My5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kMDg0YTc4NDUzYzg3NTEzYjZmY2Q2ZTI0MDk4NDc3NmYyMjExMTljZWYxMjE4MmU5YzYyZjIwZTZmZmE0OThjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.W8PKJeKZVKnfs6t7CWUjQ3Vz_EUeVE3joaqQE48yutU" alt="HQaPT2Wa0AAZi5R.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/173045582/642435389-36c16aa4-fcb5-4ce4-abcc-b3acbbe83209.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg5LTM2YzE2YWE0LWZjYjUtNGNlNC1hYmNjLWIzYWNiYmU4MzIwOS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wMGM5MzYzMjlkN2NjOTg4NzExZTRkOWJjMzNjNzgzY2M2OWM5YmJmNjU0ZTBiOWFkMjZhZTBjZGUyZDFlY2E3JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.4ER96g3OafqGbw9hSsqnt-6e1QbEEeN2wF5mj8rxMao"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F173045582%2F642435389-36c16aa4-fcb5-4ce4-abcc-b3acbbe83209.jpg%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODg1MDk5NDksIm5iZiI6MTc4ODUwOTY0OSwicGF0aCI6Ii8xNzMwNDU1ODIvNjQyNDM1Mzg5LTM2YzE2YWE0LWZjYjUtNGNlNC1hYmNjLWIzYWNiYmU4MzIwOS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwOTA0JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDkwNFQwODE0MDlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wMGM5MzYzMjlkN2NjOTg4NzExZTRkOWJjMzNjNzgzY2M2OWM5YmJmNjU0ZTBiOWFkMjZhZTBjZGUyZDFlY2E3JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZyZXNwb25zZS1jb250ZW50LXR5cGU9aW1hZ2UlMkZqcGVnIn0.4ER96g3OafqGbw9hSsqnt-6e1QbEEeN2wF5mj8rxMao" alt="HQaPT2KaEAEJMK0.jpg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Under the strict CPU and memory constraints of Garmin devices, implementing complex UIs—such as sun and moon orbit calculations, panoramic views, and animations—usually presents a major challenge in balancing "performance preservation" and "code maintainability."&lt;/p&gt;
&lt;p&gt;By utilizing the state management and caching mechanisms of MonkeyHooks, YAMAKAGE organizes complex state transitions and data flows to enhance maintainability, while achieving practical performance, such as smooth rendering and low power consumption.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Core Design&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;MonkeyHooks is designed based on the following paradigms:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Centralized State Management:&lt;/strong&gt;
It has a single &lt;code&gt;Store&lt;/code&gt; shared across the entire app. States are…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tomopumipumi/monkey-hooks" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Since Monkey C lacks generics, I prepared type-specific wrappers to simulate a React Hooks-like API (e.g., &lt;code&gt;useNumber&lt;/code&gt;, &lt;code&gt;useString&lt;/code&gt;). However, I fell into an unexpected trap during the initial implementation. Mimicking React components, I designed the architecture to call hooks directly inside the render loop (&lt;code&gt;onUpdate&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using MonkeyHooks as MH;

class MainView extends WatchUi.View {
    function onUpdate(dc as Graphics.Dc) as Void {
        // Problem: Calling hooks every frame (dozens of times per second) causes high CPU load in Monkey C
        var width = MH.useNumber(CoreArena.DISPLAY_WIDTH).get();
        var progress = MH.useFloat(MainArena.PROGRESS).get();

        // Render logic...
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In modern JavaScript runtimes, this wouldn't be an issue at all. But on Garmin's Monkey C VM, routing through complex getters every single frame inside &lt;code&gt;onUpdate&lt;/code&gt; unexpectedly consumed CPU time and placed a severe load on the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Cost of the Traditional Approach: Bloated View Classes
&lt;/h2&gt;

&lt;p&gt;The traditional optimization technique often recommended in the Garmin developer community is to &lt;strong&gt;cache state in the View class's member variables and read them directly in the render loop.&lt;/strong&gt; Accessing member variables, whose symbols are resolved at compile time, has a drastically lower execution cost compared to method invocations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MainView extends WatchUi.View {
    private var _width as Number = 0;
    private var _progress as Float = 0.0;
    private var _gpsStatus as String = "";

    function onLayout(dc) {
        _width = dc.getWidth();
    }

    function onUpdate(dc as Graphics.Dc) as Void {
        // Fast execution, but creates tightly coupled code
        dc.drawText(_width / 2, 100, font, _gpsStatus, ...);
        // ...hundreds of lines of render logic
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach certainly reduced the CPU load. But the trade-off was that OS lifecycle management, state update logic, and complex UI rendering logic became tightly coupled inside a single, massive class.&lt;br&gt;
The biggest problem was that &lt;strong&gt;unit testing the UI became impossible.&lt;/strong&gt; Rendering logic couldn't be isolated and verified, forcing me to launch the simulator and visually inspect the screen every time I made a minor UI tweak.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. The Architectural Shift: Container/Presenter and "Props Packing"
&lt;/h2&gt;

&lt;p&gt;To break free from this tight coupling while maintaining performance, I decided to adopt the "Container/Presenter pattern" from frontend web development.&lt;/p&gt;

&lt;p&gt;In Monkey C, passing data using Dictionaries incurs hash calculation overhead, and defining Classes consumes precious memory. Additionally, there's a strict limit on the number of arguments a function can take.&lt;br&gt;
The solution I adopted was the &lt;strong&gt;"Props Packing Pattern."&lt;/strong&gt; This involves packing multiple pieces of data into an Array—the most lightweight data structure available—and managing its indices using an Enum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 1. Define array indices using Enum to avoid magic numbers and simulate types
module MainProps {
    enum {
        W = 0,
        H,
        CX,
        PROGRESS,
        GPS_TEXT,
        DATA_SIZE 
    }
}

// 2. Container (View): Responsible ONLY for preparing and packing state
class MainView extends WatchUi.View {
    private var _props as Array = new [MainProps.DATA_SIZE];

    function onShow() {
        _props[MainProps.PROGRESS] = 0.0;
        _props[MainProps.GPS_TEXT] = "GPS: Searching...";
    }

    function onUpdate(dc as Graphics.Dc) as Void {
        // Pass the packed array to the pure rendering module
        MainRender.render(dc, _props);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the rendering side (Presenter), the received array is unpacked into local variables. This takes advantage of the fact that accessing local variables in Monkey C is processed faster than accessing member variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module MainRender {
    // Pure function for rendering. No dependency on WatchUi.View state.
    function render(dc as Graphics.Dc, props as Array) as Void {
        // Unpack to local variables for fast access
        var w = props[MainProps.W] as Number;
        var cx = props[MainProps.CX] as Number;
        var progress = props[MainProps.PROGRESS] as Float;

        MainSunAnimation.render(dc, progress, w, ...);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. The Sweet Spot of State Management: Separating Global and Local
&lt;/h2&gt;

&lt;p&gt;As I advanced this architecture, I realized that managing &lt;em&gt;all&lt;/em&gt; state in a single global store (MonkeyHooks) wasn't optimal. To prevent the store from bloating, I established clear rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cross-screen global state (Managed by MonkeyHooks):&lt;/strong&gt; Screen size, GPS coordinates, user settings, etc. These are placed in the Store, subscribed to by the View, and packed into the Props array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-level local state (Kept inside the View):&lt;/strong&gt; Animation progress rates for specific screens, tab indices, etc. These are kept out of the global store and held in the local variables of each View, which updates them autonomously and passes them to the Render module.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This hybrid approach kept the global space clean while enhancing the independence of each module.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Profiling: A 0.8ms Overhead and Hidden CPU Load
&lt;/h2&gt;

&lt;p&gt;To verify the optimization results, I used the simulator's profiler to compare the execution times of the three patterns. Here are the results for Total Time per frame and pure CPU Time consumed by the functions themselves in a release build:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Total Time / Frame&lt;/th&gt;
&lt;th&gt;CPU Time / Call&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct Hooks Call&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~ 19.71 ms&lt;/td&gt;
&lt;td&gt;~ 86.6 μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Member Variable Cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~ 19.77 ms&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Props Packing (Array)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~ 20.58 ms&lt;/td&gt;
&lt;td&gt;~ 58.3 μs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;(Note: CPU Time for Props Packing is the sum of array packing on the View side (6.1 μs) and variable unpacking on the Render side (52.2 μs).)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Surprisingly, the Props pattern, which I believed to be the most optimized, was actually about &lt;strong&gt;0.8 ms&lt;/strong&gt; slower in overall frame processing time. This was likely due to the accumulated costs of VM overhead from array operations and type casting (e.g., &lt;code&gt;as Number&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;However, looking at the pure CPU calculation load (CPU Time), &lt;strong&gt;the Props pattern, which packs and unpacks arrays, is about 1.5 times lighter&lt;/strong&gt; than calling Hooks every time. The logic itself was actually running extremely efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The 0.8ms Trade-off: Enabling Emulator-Free Automated Testing
&lt;/h2&gt;

&lt;p&gt;A delay of 0.8 ms per frame is imperceptible to the human eye, and its impact on battery consumption is well within the margin of error.&lt;br&gt;
Accepting this tiny overhead dramatically improved the developer experience. Because &lt;code&gt;MainRender&lt;/code&gt; became a "pure function" with no dependencies on &lt;code&gt;WatchUi.View&lt;/code&gt;, &lt;strong&gt;it became possible to run headless UI integration tests and benchmarks without ever launching the simulator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By simply passing a dummy canvas and a Props array, you can automate crash testing (Smoke Tests) for any edge case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Automated UI crash testing without launching the simulator
module SmokeTests {
    (:test)
    function testMainRenderDoesNotCrash(logger as Test.Logger) as Boolean {
        var dc = MHTest.createDummyDc(240, 240); 
        var props = TestFixture.createDummyProps(); 

        try {
            // Inject normal data
            MainRender.render(dc, props);

            // Inject edge-case data
            props[MainProps.PROGRESS] = -999.0;
            MainRender.render(dc, props);

            logger.debug("MainRender executed successfully.");
        } catch (e) {
            logger.error("MainRender crashed: " + e.getErrorMessage());
            return false;
        }
        return true;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, by integrating benchmark tests into the codebase, I could mechanically detect whether new rendering logic met performance requirements (FPS). I was finally free from the anxiety of "Will changing this code crash the physical device?" and had built a solid foundation for safe refactoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Conclusion
&lt;/h2&gt;

&lt;p&gt;Garmin WatchApp development comes with severe hardware constraints that are almost unimaginable on modern platforms.&lt;/p&gt;

&lt;p&gt;However, strict constraints don't mean you have to resign yourself to writing untestable, massive spaghetti code. Design philosophies cultivated in modern frontend development, such as "Separation of Concerns" and "Testability through Pure Functions," are highly applicable even in legacy-like environments if you adapt the implementation strategy (like using Arrays and Enums for Props packing) to fit the ecosystem.&lt;/p&gt;

&lt;p&gt;Profiling proved that this architecture wasn't the absolute "fastest," but it allowed me to find the perfect sweet spot between execution speed and testability.&lt;/p&gt;

&lt;p&gt;How do you handle state management and testability in highly resource-constrained environments? Let me know your thoughts and insights in the comments!&lt;/p&gt;

</description>
      <category>garmin</category>
      <category>monkeyc</category>
      <category>connnectiq</category>
    </item>
    <item>
      <title>Forget AI. I Boosted My Productivity with a VSCode Extension That Throws Sushi.</title>
      <dc:creator>Ichimura Tomoo</dc:creator>
      <pubDate>Fri, 14 Aug 2026 07:54:33 +0000</pubDate>
      <link>https://dev.to/tomopumipumi/forget-ai-i-boosted-my-productivity-with-a-vscode-extension-that-throws-sushi-4lgb</link>
      <guid>https://dev.to/tomopumipumi/forget-ai-i-boosted-my-productivity-with-a-vscode-extension-that-throws-sushi-4lgb</guid>
      <description>&lt;p&gt;Every day, the tech world screams about how AI is going to 10x our productivity, write our entire codebase, and solve all our problems. But let's be real—AI doesn't solve the core issue: &lt;em&gt;staring at a screen for 8 hours is exhausting, and sometimes your brain just gives up.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, instead of relying on yet another LLM tool, I decided to tackle developer burnout directly. I created a VSCode extension called &lt;code&gt;SUSHI-Theme&lt;/code&gt; for developers whose souls have been temporarily crushed by coding.&lt;/p&gt;

&lt;p&gt;It does exactly one thing: "Sushi flies out as you type." That's it. That's the entire extension...&lt;/p&gt;

&lt;p&gt;In the GIF above, it's only &lt;code&gt;Ikura&lt;/code&gt; (salmon roe), but we also have &lt;code&gt;Maguro&lt;/code&gt; (tuna), &lt;code&gt;Ebi&lt;/code&gt; (shrimp), and more.&lt;/p&gt;

&lt;p&gt;Depending on your parameter adjustments, it might get visually obnoxious and completely ruin your code's readability. But when you're &lt;em&gt;that&lt;/em&gt; exhausted, your brain isn't processing the code anyway. You might as well sit back, stare blankly at flying seafood, and let it heal your weary mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sushi Indicator
&lt;/h3&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%2F29pf520rghi6rsmiispo.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%2F29pf520rghi6rsmiispo.png" alt=" " width="532" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fever Mode
&lt;/h3&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%2F2a6llj0ol5ff3m90p72c.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%2F2a6llj0ol5ff3m90p72c.gif" alt=" " width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Completely Over-Engineered
&lt;/h2&gt;

&lt;p&gt;To balance the &lt;strong&gt;majestic trajectory of flying sushi&lt;/strong&gt; with the &lt;strong&gt;lightweight performance expected of a completely unnecessary joke extension&lt;/strong&gt;, I refused to use a heavy game engine.&lt;br&gt;
Instead, I developed a lightweight physics engine from scratch. &lt;em&gt;Just for the sushi.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Aside from the VSCode API, the built extension consists entirely of my own custom code with absolutely zero external dependencies. Artisanal, hand-crafted sushi physics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;The default settings are configured to be relatively unobtrusive to your actual job.&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%2Fuuc8s6u7dvm28itt15an.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%2Fuuc8s6u7dvm28itt15an.gif" alt=" " width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But for those who think, "That's boring!", I've prepared a buffet of customizable parameters so you can absolutely sacrifice your PC's performance for entertainment.&lt;/p&gt;

&lt;p&gt;You can access the settings screen from here:&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%2Fl9ss722ihwvdy5mzb7b5.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%2Fl9ss722ihwvdy5mzb7b5.gif" alt=" " width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic (Display / Type)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.effectType (Falling Toppings)&lt;/strong&gt;
Choose the type of sushi weaponized by your keystrokes. The default is &lt;code&gt;random&lt;/code&gt;, but you can set it to rain nothing but your "favorite topping"—like maguro, ikura, ebi, or matcha—endlessly. Perfect for those moments when you think, "I don't want to write TypeScript, I just want tuna."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.enableStatusBar (Sushi Memory Indicator Display)&lt;/strong&gt;
Toggles the status bar feature that visually represents your PC's memory usage via the number of sushi pieces. Because why use task manager?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleLifespanMultiplier (Sushi Freshness / Display Time)&lt;/strong&gt;
A multiplier for how long the sushi stays on the screen before rotting (despawning). Crank this up, and sushi will endlessly pile up at the bottom of your screen like an abandoned conveyor belt restaurant.
&lt;em&gt;&amp;gt; Note: Please watch your RAM. Sushi memory leaks are real.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Physics Engine Tuning (Movement / Walls / Collisions)
&lt;/h3&gt;

&lt;p&gt;Play god with the laws of sushi physics.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.enableParticleCollision (Sushi Collision Detection)&lt;/strong&gt;
Turning this ON makes the sushi realistically bounce off each other in a glorious seafood mosh pit. Absolutely essential if you want to build a mountain of sushi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleSpeedMultiplier (Launch Speed)&lt;/strong&gt;
Adjusts the initial velocity of the sushi the moment you type. Crank this up, and sushi will shoot out with absolute malice, like your keyboard owes you money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.bounceTopDistance (Distance to Ceiling)&lt;/strong&gt;
Sets how high above the cursor the sushi must fly before hitting the "ceiling" and bouncing back. Set it to 0 to remove the ceiling, letting your sushi ascend to the heavens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.bounceBottomDistance (Distance to Floor)&lt;/strong&gt;
Sets where the "floor" is. The larger the value, the further down they fall before bouncing. Set it to 0, and the floor vanishes, sending your sushi into the infinite abyss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.bounceLeftDistance / bounceRightDistance (Left &amp;amp; Right Walls)&lt;/strong&gt;
Sets the distance to the side walls. Set walls on all four sides, crank up the bounciness, and congratulations—you've turned your IDE into a sushi pinball machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleRestitution (Overall Bounciness)&lt;/strong&gt;
The kinetic "bounciness" when sushi hits a surface. &lt;code&gt;1.0&lt;/code&gt; makes it ricochet like a superball; &lt;code&gt;0&lt;/code&gt; makes it drop with a depressing thud, much like your motivation on a Friday afternoon.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleBouncinessMultiplier (Bounciness per Topping)&lt;/strong&gt;
Because obviously, Ikura should be bouncier than Maguro. This adjusts the individual elastic properties of different toppings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleMassMultiplier (Sushi Mass)&lt;/strong&gt;
The physical weight of the sushi. Because heavy tuna hits different in a collision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.particleFrictionMultiplier (Sushi Friction)&lt;/strong&gt;
The grip when sliding on the floor. Increase this if you want the sushi to screech to a halt after landing, rather than sliding around like it's on ice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exhilaration Adjustments (Combo / Fever Settings)
&lt;/h3&gt;

&lt;p&gt;Motivation management settings for when you just need to zone out and smash keys.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.comboUnit (Level-up Threshold)&lt;/strong&gt;
Sets how many characters you need to furiously type before the sushi effects get upgraded. Default is 5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.comboTimeoutMs (Combo Grace Period)&lt;/strong&gt;
How many milliseconds you have before your combo breaks after you stop typing. Default is 1500 (1.5 seconds). Set this longer if you have commitment issues or just hate being rushed by virtual sushi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.feverTriggerCombo (Fever Activation Quota)&lt;/strong&gt;
The number of combos required to trigger the legendary Fever Time. Default is 50 combos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.feverDurationMs (Fever Duration)&lt;/strong&gt;
How long Fever Time lasts (Default: 10000 = 10 seconds). If you make this too long, your editor turns into a blinding seafood disco and you literally will not be able to code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sushiTheme.feverSpawnCount (Sushi Volume during Fever)&lt;/strong&gt;
How many pieces of sushi drop &lt;em&gt;simultaneously per keystroke&lt;/em&gt; during Fever mode. Even the default of 5 is a borderline catastrophic amount of fish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Performance Settings (Mercy for your PC)&lt;/p&gt;

&lt;p&gt;Frying your CPU in the pursuit of "healing" completely defeats the purpose, so you can throttle the load here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;sushiTheme.fps (Frame Rate)&lt;br&gt;
Choose your framerate: 15, 30, 60, or 120. 60 or 120 gives you buttery smooth flying fish, while 15 or 30 is great if you actually care about your laptop's battery life.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: VS Code might bottleneck high FPS settings anyway, because it's a code editor, not a PlayStation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sushiTheme.throttleMs (Rendering Throttle Interval)&lt;br&gt;
The minimum rendering interval (in milliseconds) to prevent the editor from lagging when you type furiously. If you feel typing lag, increase this to 100–150 to lighten the load and save your sanity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;I recently introduced SUSHI-Theme into our company's enterprise project, and the results have been staggering. The sheer dopamine hit of triggering "Fever Mode" during a high-stakes production hotfix motivated the team so much that our development efficiency actually surpassed AI. Our lead engineer hasn't slept in three days, sustained purely by the visual thrill of bouncing salmon roe.&lt;/p&gt;

&lt;p&gt;(Note: Do not actually force your team to use this on a production codebase unless you want them to quit.)&lt;/p&gt;

&lt;p&gt;Please install the SUSHI-Theme extension, ruin your productivity (in a good way), and let flying seafood soothe your weary developer soul whenever you're tired of coding!&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/tomopumipumi/SUSHI-Theme_ForVSCode" rel="noopener noreferrer"&gt;https://github.com/tomopumipumi/SUSHI-Theme_ForVSCode&lt;/a&gt;&lt;br&gt;
VSCode Extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=ichimura-tomoo.sushitheme" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=ichimura-tomoo.sushitheme&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sushi</category>
      <category>typescript</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
