highascg

Walkthrough — live-editable pixel-map fixtures (HighAsCG JS pipeline)

Badge: [DEPRECATED — legacyJsPixelmap flag] · [LIVE-EDITABLE] — everything here re-applies on save with no Caspar restart and no config regen. Engine: WO-179 sampling pipeline (src/sampling/, src/artnet/, client/lib/dmx-state.js). Cites are file:line in this repo. Landscape / engine choice: ARTNET_PIXEL_MAPPING.md

WO-242: this whole engine is deprecated in favor of native pixelmap screen destinations (dedicated Caspar channel + native <artnet> consumer — see WALKTHROUGH_ARTNET_LED_WALL.md and the “Pixel-map screen (native) — primary flow” section of ARTNET_PIXEL_MAPPING.md). The code below still works exactly as documented, but its fixture-creation UI is now hidden unless settings.ui.legacyJsPixelmap is set to true. (Note: the Device View “mapping nodes” band — “+ Add mapping node” button — is a separate canvas-splitting feature and remains fully active; see the Pixel Map tab inspector and ARTNET_PIXEL_MAPPING.md for clarity.) Only this engine gives you sACN output and restart-free live remapping — the native path is Art-Net only and requires a config regen + Caspar restart per change. Re-enable the flag if you specifically need those two properties; otherwise use the native flow.

Use this engine when the mapping changes during the show: pixel tape you re-aim after a set change, house-light sACN washes, anything you want to nudge from FOH mid-act.


1. How it runs (so you know what you’re operating)

  1. When settings.dmx.enabled is true and fixtures exist, the server attaches a sampling tap per source program channel: default is Caspar ADD <ch> STREAM udp://… (MPEG-TS) decoded by a local ffmpeg with scale=…:flags=area (src/sampling/dmx-sampling-ingress.js:137-160); fallback/opt-in FILE mode uses a raw-RGB FIFO on dedicated consumer slot 97 (dmx-sampling-ingress.js:17,217-250) — both are dynamic AMCP consumers, not config-file consumers.
  2. The tap is downscaled to 10–50 % of PGM, sized from the densest fixture grid ×2 safety margin (src/sampling/dmx-sampling.js:60-86).
  3. A worker thread samples each fixture’s grid cells (center pixel or region average), applies brightness → gamma LUT → colorOrder (src/sampling/sampling-worker.js:60-172).
  4. DmxOutput.send() transmits per fixture: Art-Net via dmxnet or sACN via the sacn package (src/sampling/dmx-output.js:26-91).
  5. Every settings save re-runs SamplingManager.updateConfig() live (index.js:316); it only restarts its own taps when the channel plan actually changed (dmx-sampling.js:120-161).

2. The fixture model (what you are editing)

Stored in settings.dmx.fixtures; created with these defaults (client/lib/dmx-state.js:69-99):

{
  "id": "fixture_xyz",
  "sample": { "x": 0, "y": 0, "w": 200, "h": 200 },
  "rotation": 0,
  "mirrorH": false,
  "mirrorV": false,
  "sampleMode": "average",
  "sourceChannel": 1,
  "grid": { "cols": 1, "rows": 1 },
  "colorOrder": "rgb",
  "universe": 1,
  "startChannel": 1,
  "protocol": "artnet",
  "destination": "127.0.0.1",
  "gamma": 2.2,
  "brightness": 1.0
}

3. UI steps (as the code has them today)

  1. Enable the engine: settings.dmx.enabled + fps (+ debugLogDmx). The settings modal currently passes the dmx object through untouched (client/components/settings-modal-logic.js:108) — there is no dedicated enable checkbox in the modal yet; flip it via the settings API (POST /api/settings with dmx.enabled: true) or devtools dmxState.setEnabled(true). Honest gap, see §6.
  2. Pixel Map tab: the workspace has a pixelmap tab (client/index.html:91, activation wiring client/app.js:124-127). While it is active, the main Inspector panel hosts the fixture properties editor (client/components/inspector-panel.js:47-52,74-79).
  3. Fixture properties (client/components/fixture-inspector.js:33-71): Name, Universe, Start Ch, Protocol (Art-Net / sACN dropdown), Destination, Color order, Source channel, Grid cols/rows, Brightness, Rotation (°), Mirror H / Mirror V checkboxes, Sample mode (Single pixel (center) / Region average), Delete fixture. Every field saves debounced (450 ms, dmx-state.js:22,154-170) and the server re-applies live.
  4. Creating a fixture — current gap: dmxState.addFixture() exists (client/lib/dmx-state.js:69) but no button in the current build calls it, and the “DMX Fixtures” template section of the mapping browser (client/components/pixel-mapping-browser.js) is not mounted. Today fixtures are created by writing settings.dmx.fixtures (settings API) or dmxState.addFixture({...}) from the browser console; after that, all editing is first-class UI via the Inspector.
  5. Don’t confuse it with the Mapping Preview overlay (client/components/pixel-map-editor.js): that editor (toolbar “Mapping Preview” / “Delete Slice” / “Close Preview”) belongs to Device View pixel-mapping nodes — video slicing onto DeckLink/GPU outputs (src/config/pixel-mapping-config.js) — not DMX fixtures.
  6. Live feedback: sampled colors stream to the UI over WebSocket dmx:colors (src/sampling/dmx-sampling.js:363-369, client/lib/app-ws-handlers.js:122). For on-console verification enable debugLogDmx: once per second the app log prints [DMX] debug: <id> artnet→<dest> u<uni>@<start>: [ch,ch,…] (dmx-sampling.js:341-361).

4. Live re-mapping mid-show

This is the whole point: change sample x/y/w/h, rotation, mirror, grid, universe, even protocol — the save debounce fires, the server updates config, and DMX follows within ~a second. No Caspar restart; the tap itself only re-arms when the source-channel plan changes (different channel, denser grid), which costs a sub-second REMOVE/ADD of the sampling consumer only — program output is untouched.

Watch the app log for the pipeline’s own lines:

5. Inputs (the other direction)

6. Honest limits (numbers from code)

7. NOT YET — planned, don’t look for it in the UI