Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dev Mode

Byonk includes a development mode that provides a web-based device simulator with live reload capabilities, making it easier to develop and test screens.

Starting Dev Mode

# Start with dev mode enabled
byonk dev

# With external screens directory for live reload
SCREENS_DIR=./screens byonk dev

Once started, open your browser to http://localhost:3000/dev to access the device simulator.

Dev Mode Screenshot

Features

Device Simulator

The simulator displays your rendered screens in a visual frame resembling a TRMNL device. You can:

  • Select a screen from the dropdown (populated from config.yaml and auto-discovered screens)
  • Select a device to auto-load its configured screen, parameters, panel, and dither settings
  • Simulate device context: battery voltage, WiFi RSSI, and time override
  • View the rendered PNG exactly as it would appear on the device
  • Pixel inspector: hover over the image to see a magnified view

Live Reload

When SCREENS_DIR is set to an external directory, the dev mode watches for changes to .lua and .svg files. When you save a file:

  1. The file watcher detects the change
  2. An event is sent to connected browsers via Server-Sent Events (SSE)
  3. The screen automatically re-renders with the latest code

Custom Parameters

The dev UI includes a JSON editor for passing custom parameters to your Lua scripts. These are available in your script via the params table.

Error Display

Errors are displayed in a console below the device preview, including Lua syntax/runtime errors, template errors, and render failures.

Display Calibration

Dev mode provides tools for calibrating dithering to match your physical display. Changes made in the dev UI are synced live to the actual device — what you tune is what the device shows.

Dither Algorithm Selection

The dither dropdown lets you try all 9 algorithms on your content:

  • atkinson (default) — Atkinson error diffusion (75% propagation)
  • atkinson-hybrid — Atkinson with hybrid propagation (100% achromatic / 75% chromatic)
  • floyd-steinberg — Floyd-Steinberg with blue noise jitter
  • jarvis-judice-ninke — wide 12-neighbor kernel
  • sierra, sierra-two-row, sierra-lite — Sierra family
  • stucki — wide 12-neighbor kernel similar to JJN
  • burkes — 7-neighbor kernel, good balance of speed and quality

Dither Tuning Controls

The Render Options panel exposes three tuning parameters:

ControlEffect
Error clampCaps how much accumulated error one pixel may carry into its neighbours. The default is 1.0 — full scale in a channel. Lowering it suppresses diffusion, which quietens oscillation but makes saturated areas render flat.
Noise scaleControls blue noise jitter strength. Higher values break “worm” artifacts more aggressively.
Chroma clampLimits chromatic error propagation. Prevents color bleeding on chromatic palettes.

Color Calibration

Click any actual-color swatch to open the HSL adjustment popup. Adjust hue, saturation, and lightness with live preview to match what your panel really displays. The adjusted colors_actual string can be copied to config.yaml.

Live Device Sync

When you select a device entry and adjust dither algorithm, tuning parameters, or measured colors, changes are synced to the production /api/display handler. The physical device picks up the new settings on its next refresh.

Calibrator Screen

Byonk ships a built-in calibration screen (byonk-builtin/calibration/color) designed specifically for display calibration. Assign it to your device temporarily while tuning:

devices:
  "ABCDE-FGHJK":
    screen: byonk-builtin/calibration/color
    panel: my_panel
    dither: atkinson

The calibrator shows everything you need to evaluate dithering quality:

  • White-to-color gradients for each palette color — reveals error diffusion artifacts, oscillation, and color bleeding
  • Full hue sweep at 100% saturation — shows how the ditherer maps arbitrary colors to your limited palette
  • Test photo — real-world image to judge overall photo reproduction
  • Solid color patches with hex labels — compare what the panel actually displays against the expected color values

Use the calibrator on your physical device while adjusting tuning in dev mode — the live sync means every change you make is immediately visible on the display.

Gamut Patch Screen

byonk-builtin/calibration/gamut answers a narrower question: which colors can this panel actually mix, and which does it give up on?

devices:
  "ABCDE-FGHJK":
    screen: byonk-builtin/calibration/gamut
    panel: my_panel
    params:
      hues: 24      # hue columns around the full circle (2-48)
      levels: 6     # lightness rows (1-12)

It draws the hue circle as isolated flat patches rather than the calibrator’s smooth gradient. That difference is the point: in a gradient, neighbouring hues bleed together, so a hue the panel cannot reproduce still looks like it is doing something. Here each patch stands alone, so you can read it directly:

  • A speckled patch — the ditherer mixed several palette colors to approximate the request. This is what working output looks like.
  • A solid patch — the ditherer picked one palette entry for every pixel. On a 6-color panel expect solid blue across roughly 225°–270°: that genuinely is the best the palette offers, not a bug.
  • A solid white patch (drawn outlined, so it doesn’t read as a missing cell) — the request collapsed to white entirely. Cyan around 180° does this on 6-color panels, whose bluest and greenest inks are both dark.

Rows vary lightness because reachability depends on it — a hue may mix cleanly when dark and collapse when light.

Tone Marker A/B Screen

byonk-builtin/calibration/tone answers a different question again: what does the gamut mapper actually change on real content, on your real panel?

devices:
  "ABCDE-FGHJK":
    screen: byonk-builtin/calibration/tone
    panel: my_panel
    params:
      hues: 12      # patch grid hue columns (2-48)
      levels: 5     # patch grid rows (1-12)

It renders the same content — a photograph, a hue sweep, and a colour patch grid, top to bottom — twice, side by side. The two columns are identical markup with one difference: only the right-hand column is marked data-byonk-tone="continuous". That mark drives three things at once — the right column is matched against the panel’s measured colours and gamut mapped, while the left column is matched against the official palette and exact-match pinned (see Marking continuous-tone content). The left column is the untouched control. Whatever visibly differs between the two columns on your device is what that whole difference in treatment is doing to your content. The hue sweep is a fixed gradient of 12 equal hue steps, not driven by either param.

There are no gamut-mapping knobs on this screen — it deliberately shows you what a real screen gets, not a tuning surface.

Calibration Workflow

  1. Assign the byonk-builtin/calibration/color screen to your device in config.yaml
  2. Select your device in dev mode — this loads its screen, panel, and dither settings
  3. Choose a dither algorithm that works well for your content type
  4. Adjust tuning parameters (max_error, noise_scale, chroma_clamp, strength) until the preview looks good
  5. Calibrate measured colors by clicking actual-color swatches and adjusting HSL to match the solid patches on the physical display
  6. Verify on device — changes sync automatically; wait for the next device refresh
  7. Commit to config — copy the values to config.yaml and switch back to your normal screen:
panels:
  my_panel:
    name: "My Panel"
    colors: "#000000,#FFFFFF,#FF0000,#FFFF00"
    colors_actual: "#303030,#D0D0C8,#C04040,#D0D020"  # from dev mode calibration

devices:
  "ABCDE-FGHJK":
    screen: examples/gphoto
    panel: my_panel
    dither: floyd-steinberg
    max_error: 1.0    # from dev mode tuning
    noise_scale: 0.5    # from dev mode tuning

Tuning values can also be set per-script in the Lua return table — see Lua API.

Configuration

Dev mode uses the same environment variables as the normal server:

VariableDescriptionDefault
BIND_ADDRServer bind address0.0.0.0:3000
SCREENS_DIRExternal screens directory (enables live reload)(embedded)
FONTS_DIRExternal fonts directory(embedded)
CONFIG_FILEExternal config file(embedded)

Example Workflow

  1. Extract embedded assets to work with:

    byonk init --all
    
  2. Start dev mode with external screens:

    SCREENS_DIR=./screens CONFIG_FILE=./config.yaml byonk dev
    
  3. Open http://localhost:3000/dev in your browser

  4. Select the screen you want to work on

  5. Edit your Lua script or SVG template — changes appear automatically

  6. Use the calibration tools to tune dithering for your panel

  7. Check the console below the preview if something goes wrong

Differences from Production

Dev mode includes a few differences from the production byonk serve command:

  • Additional /dev/* routes for the simulator UI
  • File watching enabled (when using external SCREENS_DIR)
  • No content caching — always renders fresh content
  • More verbose logging by default
  • Tuning and color overrides are session-only (reset on server restart)