This file records hard-won lessons, quirks, and decisions that are easy to forget.
- Settings persistence: The simulator caches settings in a binary
REACTOR.SETfile under$TMPDIR/com.garmin.connectiq/GARMIN/APPS/SETTINGS/. Changing defaults inproperties.xmland recompiling does NOT override the cached values. You must deleteREACTOR.SETbefore relaunching. Theset_theme.shscript handles this automatically. - App Settings Editor: Opening the App Settings Editor (
Cmd+P) from amonkeydo-launched session shows "No settings file found". This is normal when not using VS Code's Connect IQ extension. Useset_theme.shinstead. - Clean builds: When changing
settings.xml,properties.xml, ordrawables.xml, always deletebin/andgen/before rebuilding to clear stale generated code. Theset_theme.shscript now automates this step to avoid dirty cache errors. - OneDrive Dataless Files Lock: macOS OneDrive can randomly lock
.pngfiles (e.g.,Operation timed out/Could not process image for bitmap). To fix this, runpython scripts/fix_svg_icons.pyto cleanly regenerate the PNGs from your local SVG files.
- Chart Y offset:
chartTopY = 44(originally 64) was specifically chosen to avoid clipping by the circular screen bezel. - Chart gap: The 18px gap between left and right charts aligns with the battery widget centerline.
- Header Widget: The top battery widget is centered vertically at
Y=14, creating a perfect balance between the top bezel edge (Y=0) and the top of the charts (Y=44). - Central Layout Shift: The entire central block (
ChartWidget,TimeWidget,BottomMetricsWidget) was shifted up by 20 pixels to better balance the watchface vertically. - TimeWidget Date Size: The text for day and month uses a font height of 22 (originally 14, then 18). The day number now uses
SegmentRendererat size 14x24 for all themes, eliminating the small VFD bitmaps. - TimeWidget Frame: The outer cyan frame thickness was reduced from 4px to 3px for a slightly finer look.
- TimeWidget separator: The horizontal line between calendar and clock is dynamically calculated to span from the left edge of window 1 to the right edge of window 3.
- Flank segment logic: Maps metric values (HR: 60–180, Stress: 0–100, etc.) into 10 discrete LED-style segments.
- Chart Styles: The
ChartWidgetdynamically supports 5 drawing modes for historical data (Solid, Dot Matrix, Stepped Line, Oscilloscope, LCD Outline) without relying on bitmaps. - SensorHistory Limitations:
Toybox.SensorHistorydoes not expose agetRespirationRateHistorymethod. Respiration rate is only available as a current reading inToybox.Sensor, so it cannot be plotted on the historical charts. Atmospheric pressure was successfully added instead. - Nixie tubes are sacred: The VFD bitmap rendering path (
THEME_NIXIE_CYAN) must never be modified or broken. The base bitmaps inresources/drawables/vfd/are the original Nixie tube art, and are processed viascripts/process_nixie_images.pyto add a wire mesh, bloom, and unlit filament background to distinguish them from the LCD vectors. - True Nixie Tube Mode: Added
THEME_NIXIE_AMBER(Theme 10) which uses a set of ultra-high-resolution Nixie digits cropped tight (120px top, 50px bottom removed before scaling) to 72x104, giving a highly authentic amber gas-discharge glow that fills the entire digit space. - SegmentRenderer for LCD: All non-Nixie themes use the procedural
SegmentRendererwhich draws 7-segment digits using filled polygons. Zero bitmaps, resolution-independent. - CompactFont: Custom procedural font for date windows and bottom metrics. Extended to support
0-9,%and all A-Z characters using rectangles and lines only — no curves. - Metric Icons (BMFont): All 30 configurable metric icons (steps, stress, floors, etc.) are rendered using a single
BMFontatlas (MetricsIconsFont) generated from Material Design Outlined icons. This replaces 30+ individual PNGs, saving massive amounts of RAM and compilation time, while allowing dynamicdc.setColor()tinting. - Battery font: Reverted to system
Graphics.FONT_XTINYbecause custom square vector fonts looked unnatural for standalone digits. - Flank font: Uses native
Graphics.FONT_XTINYfor numeric readouts — aligns well without being overly dominant.
- BMFont Atlas: All UI icons are packed into a single BMFont atlas (
metrics_icons.png), eliminating the overhead of loading and cachingWatchUi.BitmapResourceobjects. - No allocations in draw loop: Renderers reuse passed structures and primitives. No new objects are created during
onUpdate(). - Lazy loading: VFD bitmaps (
_vfdDrawables) and theMetricsIconsFontare loaded on first use, not duringinitialize().
- v1: Single hardcoded Nixie Cyan style.
- v2: Added
digitStyle(Tube vs Solid) +themeColor(5 colors) — two separate settings. This was confusing because you could select a color for Nixie mode (which had no effect). - v3 (current): Unified into a single
themeStylesetting (0–10). Value 0 = VFD Cyan bitmaps, values 1–5 = LCD segments, value 10 = True Nixie Amber bitmaps. Clean, no invalid combinations possible.
- On-Device Menu (
ReactorSettingsMenu.mc): Implements a fully localized, hierarchical on-device configuration menu (Menu2). - Options Exposed: Theme Style (VFD/LCD), Top Battery Toggle, Left/Right Flank Metrics (7 options), and Bottom Slots 1-4 (30 options).
- Localization: Hardcoded Spanish strings were completely removed from code. All menu labels and metric names dynamically load from
resources/strings/strings.xmlandresources-eng/strings/strings.xmlviaWatchUi.loadResource(). This ensures the UI automatically adapts to the watch's system language (Spanish or English).
- AOD Burn-In Protection: Implemented in
TimeWidget.mcand other widgets. When AMOLED devices (like Fenix 8) enter low-power sleep mode, heavy graphics are skipped and only the time/minimal date are drawn. The time text position shifts slightly based on minutes(min % 7)to prevent burn-in. - Dynamic Flank Gradients: Flank gauges for severity-based metrics (Battery, Heart Rate, Stress) are dynamically colorized segment-by-segment (e.g., green -> amber -> red) based on the user's realtime data, rather than being a static color.
- Complications API & Touch: SDK targeted to
minSdkVersion="4.2.0". AddedReactorDelegate.mcextendingWatchUi.WatchFaceDelegatewithonPress(). Tapping flanks or bottom metrics intercepts pixel coordinates and triggersToybox.Complications.exitTo(id)to open native Garmin apps like Heart Rate or Stress. - Partial Updates on AMOLED: Explicitly skipped. AMOLED screens do not support the legacy
onPartialUpdate()1Hz MIP callback. High power 1Hz updates happen natively during the wrist gestureonUpdate(). - Industrial Custom Fonts: Replaced
Graphics.FONT_XTINYwith our proceduralCompactFont.mcvector class for all small numeric readouts.CompactFontwas extended to support%and.using zero-memory polygon rendering, preserving the Nixie/LCD aesthetic without heavy bitmap fonts.
-
2026-08-06: Local build artifacts created during development:
bin/reactor.prg— runtime binary suitable for sideloading to a device (via MTP client/OpenMTP on macOS).bin/reactor.iq— signed release package for Connect IQ Store distribution.
-
macOS 26 MTP note: macOS does not mount MTP devices under
/Volumes. Use OpenMTP or another MTP client to transferbin/reactor.prgwhen developing on macOS. -
If you sideloaded with OpenMTP manually, verify the file placed on the device is
reactor.prgand that you copied it into the APPS/GARMIN path exposed by the MTP client.
- AMOLED Exclusive: To ensure a premium experience, maintain the industrial/retro look, and prevent layout issues caused by absolute pixel coordinates, we exclusively target AMOLED devices (Fenix 8, Epix 2, Forerunner 265/965, Tactix 7 AMOLED). MIP devices (like Fenix 7, Enduro 3) are intentionally excluded to avoid dithering (64-color limit), missing anti-aliasing, and incorrect scaling of pixel-perfect designs.