# Orion Lighting Studio V1.4 — fidelity and coverage

38 of 46 CSV rows are selectable. 8,512 sampled native C++ / browser WebAssembly comparisons passed across 304 configurations. [Machine-readable report](verification.json).

## Method and practical limits

The browser executes the recovered C++ pattern bodies through WebAssembly. A separate native executable compiles those same bodies and compares RGBW frames after source brightness and show calls. This checks portability and the browser adapter, not physical hardware equivalence or an independently authored reference implementation.

The adapter supplies simulated millis/delay, RGBW storage, geometry, and a FastLED show sink. Original snapshots are hashed and preserved locally. Build preparation removes includes/preprocessor guards and duplicate default-argument specifications to combine the files; circle's static positions array is given a fixed 90-pixel capacity. Executable pattern logic is retained. NUM_LEDS equals the selected active length; no reduced-currentLength/longer-capacity configuration is modeled.

Host int arithmetic is 32-bit, with fixed uint8/int8/uint16 fields. This is not an AVR emulator. Physical FastLED color correction, dithering, output scaling details, power limiting, wiring byte order and MCU data-transfer overhead are not simulated. Screen RGB adds the white channel and clamps; exposure affects only the rendered view.

Cold start is deterministic. All static function state starts fresh when changing pattern or parameters. Hardware mode-to-mode retained state is not emulated. Pulse calls use an assumed 10 ms loop; timer-driven functions use 1 ms polling and their original strict/non-strict comparisons. Explicit source delays are honored. Each study resets after 60 simulated seconds; the 16-bit timer overflow regime is deliberately outside this release.

The report covers 66,77,78,90 LEDs at levels 1 and 5 with orientation=length−8 and forward direction, at 28 sample times from 0 to 7,999 ms including timer boundaries. Other orientations, levels, custom colors and reverse are editable but are not covered by that full comparison matrix. No claim of footage-calibrated timing or photometric accuracy is made.

## Geometry and original colors

Configuration defaults to 66 LEDs; source comments also document 77/78 and 90. BUCKLE_LENGTH=8 is an estimate in LED-equivalent units; FLASHLIGHT_LENGTH=16. Wrapped view is a schematic ellipse, not measured hardware. Selected length is treated as both capacity and active length.

RGBW colors retain original channels, including white=(0,0,0,255), whiteMax=(255,255,255,255), red=(255,0,0,0), redOrange=(255,70,0,0), orange=(255,102,0,0), constructionGreen=(227,252,3,0), cyan=(0,255,255,0), yellow=(255,255,0,0), blue=(0,0,255,0), green=(0,255,0,0). Brightness levels map to 51,102,153,204,255, except source-specific behavior such as variable-width flashlight (full output, width changes) and speed-linked pulse (fixed maximum, speed changes).

## Implemented patterns

| CSV state | Original function call | Source file | Timing |
|---|---|---|---|
| solid_w | pattern_solid_f(currentLength, white) | patterns_core.cpp | Source delays / simulated polling |
| solid_cg | pattern_solid_f(currentLength, constructionGreen) | patterns_core.cpp | Source delays / simulated polling |
| solid_r | pattern_solid_f(currentLength, red) | patterns_core.cpp | Source delays / simulated polling |
| pulse_w | pattern_pulse_f(currentLength, white) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_cg | pattern_pulse_f(currentLength, constructionGreen) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_r | pattern_pulse_f(currentLength, red) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_alternating_r_b | alternating_pulse_f(red, blue, currentLength) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_alternatingSpeedCtrl_r_b | alternating_pulseSpeedCtrl_f(red, blue, currentLength) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_alternatingSpeedCtrl_o_w | alternating_pulseSpeedCtrl_f(orange, white, currentLength) | patterns_pulse.cpp | 10 ms loop assumed |
| pulse_alternatingSpeedCtrl_c | alternating_pulseSpeedCtrl_f(cyan, white, currentLength) | patterns_pulse.cpp | 10 ms loop assumed |
| sides_wm | pattern_sides_f(currentLength, whiteMax) | patterns_sides.cpp | Source delays / simulated polling |
| sidesBlink_r_w_b | pattern_sidesBlink_f(50,100, currentLength, red, white, blue, false) | patterns_sidesBlink.cpp | Source delays / simulated polling |
| switch_ro_w | pattern_switch_f(currentLength, 8, 1000, white, redOrange) | patterns_switch.cpp | Source delays / simulated polling |
| switch_b_w | pattern_switch_f(currentLength, 8, 1000, white, blue) | patterns_switch.cpp | Source delays / simulated polling |
| switch_r_b | pattern_switch_f(currentLength, 8, 250, red, blue) | patterns_switch.cpp | Source delays / simulated polling |
| switchB_o | pattern_switchB_f(currentLength, orange, orange, 200) | patterns_switchB.cpp | Source delays / simulated polling |
| switchB_r_b | pattern_switchB_f(currentLength, red, blue, 125) | patterns_switchB.cpp | Source delays / simulated polling |
| switchB_c | pattern_switchB_f(currentLength, cyan, white, 125) | patterns_switchB.cpp | Source delays / simulated polling |
| bounce_ro_w | pattern_bounce_f(currentLength, redOrange, white) | patterns_bounce.cpp | Source delays / simulated polling |
| strobe_r_w_b | pattern_strobe_f(35, currentLength, red, white, blue) | patterns_strobe.cpp | Source delays / simulated polling |
| strobe_ro_w | pattern_strobe_f(50, currentLength, redOrange, black, white) | patterns_strobe.cpp | Source delays / simulated polling |
| strobeB_r_b | pattern_strobeB_f(currentLength, red, blue, 50) | patterns_strobeB.cpp | Source delays / simulated polling |
| strobeB_y_w | pattern_strobeB_f(currentLength, yellow, white, 50) | patterns_strobeB.cpp | Source delays / simulated polling |
| strobeB_r_w | pattern_strobeB_f(currentLength, red, white, 50) | patterns_strobeB.cpp | Source delays / simulated polling |
| strobeB_b_w | pattern_strobeB_f(currentLength, blue, white, 50) | patterns_strobeB.cpp | Source delays / simulated polling |
| strobeD_r_w_b | pattern_strobeD_f(50, currentLength, red, white, blue) | patterns_strobeD.cpp | Source delays / simulated polling |
| circle_r_w | pattern_circle_f(14, 30, currentLength, red, black, white) | patterns_circle.cpp | Source delays / simulated polling |
| circle_r_w_b | pattern_circle_f(14, 50, currentLength, red, white, blue) | patterns_circle.cpp | Source delays / simulated polling |
| circle_g_w_g | pattern_circle_f(28, 50, currentLength, green, white, green) | patterns_circle.cpp | Source delays / simulated polling |
| pulse_flashlight_cg | pattern_pulse_f(currentLength, constructionGreen, true) | patterns_pulse.cpp | 10 ms loop assumed |
| flashlight_wm | pattern_solid_f(currentLength, black, true) | patterns_core.cpp | Source delays / simulated polling |
| flashlightB_wm | pattern_flashlightB_f(currentLength) | patterns_flashlightB.cpp | Source delays / simulated polling |
| headlamp | pattern_solid_f(currentLength, red, true) | patterns_core.cpp | Source delays / simulated polling |
| headlamp_blink | pattern_backBlink_f(currentLength, red, true) | patterns_backBlink.cpp | Source delays / simulated polling |
| flashlightSidesBlink | pattern_sidesBlink_f(50,100, currentLength, red, white, blue, true) | patterns_sidesBlink.cpp | Source delays / simulated polling |
| flashlightStrobeC_b_w | pattern_strobeC_f(currentLength, blue, 100, true) | patterns_strobeC.cpp | Source delays / simulated polling |
| flashlightStrobeC_y_w | pattern_strobeC_f(currentLength, yellow, 100, true) | patterns_strobeC.cpp | Source delays / simulated polling |
| flashlightStrobeC_r_w | pattern_strobeC_f(currentLength, red, 100, true) | patterns_strobeC.cpp | Source delays / simulated polling |

## Original quirks retained

- SwitchB's returned long-hold multiplier is overwritten on the next invocation; the visible rhythm follows the actual wrapper, not the apparent intention.
- Strobe buffer clears are not always followed by show; those stages retain the previous visible output. Brightness=0 show calls do go dark.
- BackBlink ignores its back_color argument and uses hard-coded red. Sides ignores its color argument and uses whiteMax. Their ineffective color controls are omitted.
- FlashlightB brightness levels change the number of full-output LEDs (4,10,16,22,26). Its color is hard-coded whiteMax.
- Circle num_lights uses integer division: 14 becomes 12 pixels per cluster, 28 becomes 24.
- Alternating pulse retains its 8-bit step and stage logic, including source wrap behavior. A smoother replacement would be a separately labeled creative pattern.

## Eight excluded CSV rows

| State | Reason |
|---|---|
| wave_ro_w | At i=0 source writes leds[length], outside a full-length strip. No silently corrected original is offered. |
| directionalCircle_r_w | Bounded signed-index helper and length adjustment need dedicated AVR/geometry analysis. |
| directionalCircle_ro_w | Same directional mapping boundary. |
| rotate_rainbow | Needs verified FastLED CHSV-to-RGBW conversion and inherited-brightness semantics. |
| pulse_alternating_ro_w | Disabled in every CSV profile; name also conflicts with the blue/white function arguments. |
| pulse_alternating_r_b_4 | Disabled in every CSV profile. |
| strobe_p | Disabled in every CSV profile. |
| test | Disabled diagnostic, not a production pattern. |

## Provenance and remaining work

[Source folder](https://drive.google.com/drive/folders/1JTTnxZxoeY5HsQ1QnDLV8Vz9KQ82-mJA). [Pattern CSV](https://drive.google.com/file/d/1Z0_UnM152ciiR6lHOYmhI2ppmsW0MW0F/view). [Footage folder](https://drive.google.com/drive/folders/1al2Ss6WRyIWayDycRlnFKn4L2ul-pnhJ).

Drive was read only. Historical footage is not yet decoded/calibrated. Next verification work: AVR integer semantics, retained-state transitions, more orientations/custom colors, source timer rollover, captured belt output, and FastLED library-specific behavior. Original firmware, GitHub and hardware were not modified. No serial, USB, flashing or firmware export is present.
