CueSync Strobe & Blinder
Strobes and audience blinders for Unreal Engine 5.8 with Art-Net and sACN input ports: seven fixtures, each
with a .gdtf that carries its geometry and validates against the official GDTF 1.2 schema.
Unreal imports the same file, so the render and the file share one channel layout. White, warm-white
and amber emitters, a strobe that flashes at the rate the shutter channel states, and one light per
beam group — not one light per fixture.
Version 1.0.0. Content only: no C++, nothing to compile.
Flashing lights — read before you press Play. The demo show strobes from its first seconds: the battens' cells strobe at about 23 Hz and the hybrids' tubes at about 19 Hz while they chase (0 to 7.5 s), the panels' cells at about 23 Hz (11 to 15 s), and from 21 to 24 s every fixture's shutter strobes at about 21.5 Hz (DMX 200), then runs the random shutter to 26 s. That is inside the range of about 3 to 30 flashes a second that can trigger seizures in people with photosensitive epilepsy, and a rig strobing in haze can fill the view. The show loops. Do not play it for, or in front of, anyone who has not been warned, and do not record it for an audience you do not know. On a fixture you drive yourself the pack does not limit the rate: see Flashing lights under The modes.
Quick start
- Open the project.
Lvl_Overviewopens: one of each fixture, captioned with its mode and address. - Press Play. The demo show (
LS_StrobeDemo) runs on the overview and onLvl_DemoStage, through the pack's own DMX path: a Sequencer DMX track writes the loopback output port, the port feeds the patches, the patches feed the fixtures. - To drive the rig from a console, open
Lvl_Patch(no show on it), press Play and send Art-Net or sACN to universes 60-67. (The pack's two input ports listen for both; only Art-Net has been sent to it so far, byTools/send_artnet.py, which sends Port-Address = universe: the shipped ports map universe N to Port-Address N. A desk that counts Art-Net universes from 0 must be set so the Port-Address on the wire is 60-67.) Every fixture is captioned with its label, mode and address; the.mvrcarries the same patch (so does theriglist in the Additional Files download'sfixture_manifest.json). OnLvl_DemoStage, delete or disable theCS_DemoShowactor first, or the show and your desk will fight over the same universes.
The fixtures
| Fixture | Cells | Emitters | Modes (channels) | Beams |
|---|---|---|---|---|
| CS Strobe Hybrid 12 | 12 plates + 1 tube | RGB plates, white strobe tube | Basic 6, Extended 41, Pixel 65 | 3 |
| CS Strobe Panel 32 | 32 (8 x 4) | RGBW | Basic 6, Extended 131, Pixel 195 | 4 |
| CS Strobe Batten 16 | 16 | white | Basic 2, Extended 19, Pixel 35 | 4 |
| CS Strobe Flash 1 | 1 lamp | white | Basic 2, Extended 3 | 1 |
| CS Blinder 2 | 2 | warm white + amber | Basic 4, Extended 7, Pixel 9 | 2 |
| CS Blinder 4 | 4 (2 x 2) | warm white + amber | Basic 4, Extended 11, Pixel 15 | 4 |
| CS Blinder 8 | 8 (4 x 2) | warm white + amber | Basic 4, Extended 19, Pixel 27 | 8 |
Every figure — lumens, beam and field angles, weight, power — is representative of its class of fixture, not measured from a product. The designs are generic; none is modelled on or named after any manufacturer's fixture.
The modes
- Basic — the whole fixture as one: master dimmer, shutter, then its colours (a hybrid's white drives its tube).
- Extended — master dimmer, shutter, control (its Reset range, DMX 10-19, is declared in the
.gdtf; nothing in the pack reads it); a zone's own channels (the hybrid's tube: white and its own strobe); then every cell's emitters. - Pixel — the same, with every cell's own strobe after its emitters; the Panel and the Hybrid also carry a dimmer per cell (the Batten and the Blinders have strobe only).
The fixture shutter is ranged the way a real one is: 0-31 closed (a blackout), 32-63 open, 64-223 strobe from 0.5 to 25 Hz, 224-255 random. It powers on open. A cell's own strobe opens at zero instead — 0-9 open, 10-239 strobe 0.5-25 Hz, 240-255 random — so a Pixel-mode channel a desk leaves at zero never blacks a cell out.
Flashing lights. The strobes and blinders flash at up to 25 Hz, and the pack does not limit the rate: the shutter channel is the desk's, as on a real fixture. Flashing in the range of about 3 to 30 a second can trigger seizures in people with photosensitive epilepsy, and a full-screen strobe in front of a camera is the way to hit it. Warn anyone who will watch a build of yours, and cap the rate in your own show when it will be seen by an audience you do not know.
The GDTF attribute names used in these files: ColorAdd_R/G/B/W, warm white ColorAdd_WW, amber
ColorAdd_RY (GDTF's amber), Shutter1Strobe (fixture and cell), Shutter2Strobe (the hybrid's tube).
Unreal imports the first four as Red, Green, Blue, White and keeps the rest as written.
How the fixtures work — BP_StrobeFixture
Every fixture Blueprint is a child of Blueprints/BP_StrobeFixture, which adds no C++ of its
own. While its patch is receiving:
- On the tick after new DMX, it reads every cell of its patch into two live textures, one texel
per cell — A: red, green, blue and the cell's dimmer; B: white, amber and the cell's
strobe. They are the fixture's two rows of the level's shared cell atlas
(
Blueprints/BP_StrobeCellAtlas, spawned by the first fixture that needs it), which draws every fixture's cells in one canvas pass a frame. The emitter material samples both rows at the cell's UV1 coordinate. DMX that does not change costs nothing here. A zone (the hybrid's tube, the single strobe's lamp) is one more texel after the matrix cells, fed by the fixture-level channels. A mode without a matrix (Basic) feeds every cell from the fixture-level channels. - Every tick, it drives one spot light per beam group — each blinder lamp, each quadrant of the panel, each
quarter of the batten, each side of the hybrid and its tube — from that group's own cells, in their
colour. Up to eight per fixture (
Beam0-Beam7). - It applies the fixture's master dimmer and shutter to the face and to every light: a blackout blacks the fixture out, and a strobe flashes at the rate the channel states.
How bright a light is. A beam group's light is the catalogue's candela for that group times the brightest colour
channel of its cells' emitters added together, per cell, at most 1. So full white, the RGB emitters all at full, and every
emitter at full give the same candela: the figure is the group at full white. A real RGBW fixture gives more with all its
emitters lit than with the red, green and blue alone, and a single colour emitter alone gives well under the share of the
light its sRGB colour suggests, so colour-only content from a desk lights the scene brighter here than it lights the fixture
it models. This is a simplification, stated; the lights' colour is exact (lamp luminance and light colour in
verify_in_editor.py hold it).
Before any DMX arrives the face runs a slow idle breathe, so a fixture dropped into a level is not a
black box. The per-fixture figures — cells, beam groups, candela, reach, cones — are variables on the
child's class defaults (category CueSync Strobe), written from fixture_catalogue.py (in the Additional Files download).
Maps and universes
| Map | What | Universes |
|---|---|---|
Lvl_Overview | one of each fixture, widest mode, captioned; the show runs in Play | 90 |
Lvl_DemoStage | the 44-fixture demo rig on truss, in haze; the show runs in Play | 60-67 |
Lvl_Patch | the same rig, every fixture captioned; no show | 60-67 |
The demo rig is DMX/GDTF/CueSyncStrobe_DemoRig.mvr, generated from the catalogue. It validates
against the official MVR schema (MVR 1.5; the seven .gdtf files and the .mvr were checked against
the official schemas on 2026-10-03), embeds the seven .gdtf files unchanged, and CueSync's MVR
importer reads all 44 fixtures at the right universes. It is written in the MVR's own frame
(audience at -Y), so Epic's MVR import should bring it in a quarter-turn round from Lvl_DemoStage,
which already is this rig.
The pictures on the listing show an earlier build of this pack's fixtures, truss and haze, rendered from copies of these maps. What is not in the download: the camera positions, the exposure, fog density and fill light chosen for each picture, the still look the fixtures were lit with (the demo show moves and strobes), and the title and layout of the fixture sheet. No audience, crowd, set, screen, steel support or arena is in the pictures or in the pack.
The two input ports (Config/DefaultEngine.ini) listen on every interface for universes 1-179:
Art-Net on UDP 6454, sACN on UDP 5568. They are the ports every CueSync fixture pack ships, under the
same names, so a project holding several packs needs them once. This pack uses 60-99.
If nothing lights
Press Play (a fixture reads its patch on Tick). Test on Lvl_Patch. Then: the subnet (the ports
listen on 0.0.0.0), the firewall (inbound UDP 6454 / 5568), the universe (60-67 for the rig), and the
shutter — at DMX 0-31 the fixture is closed by design. A fixture that lights its face but throws no
light: it has fewer beam groups than lights; that is per fixture, see the table.
Performance
The demo stage — 44 fixtures, 156 spot lights, volumetric haze, the show running — measured on 2026-10-03 on an
Apple M4 Pro (24 GB, macOS), in a windowed -game session at 1920 x 1080: 20.8 ms a frame (48 fps), and 19.8 ms
(50 fps) when run again. Game thread 3.6-4.3 ms, GPU 19.2-20.2 ms, of which 12.5-12.6 ms is in passes the profiler names
(lights 5.7, volumetric fog 3.4, deferred lighting 0.7, shadow depths 0.7, shadow projection 0.4); the engine's CSV
profiler, 900 frames, the median of frames 400-900 (Tools/perf_run.sh). The overview (7 fixtures, Lumen on) through the
same capture ran at 15.5 ms (65 fps) with 8.5 ms in named passes and 0.7 ms of lights, so the difference is the stage. The
machine's load average was 2.6-4.3 during the runs (other background builds); the named-pass figure does not grow when
another process loads the GPU, so compare that one. This is what one machine measured at 1080p. It is not a claim about
other machines, Windows, 4K or a GPU someone else is using, and it is a range, not a promise of what yours will do. The
24.3 ms (41 fps) of 2026-09-29, taken before the radius, housing and exposure changes, is superseded; an earlier single
capture of 12.4 ms could not be reproduced and stays withdrawn.
What keeps the cost down, and what to turn if your rig is bigger. The last column was measured on an earlier build, one GPU pass at a time; read it as indicative, not as what this build saves:
| Default | Why | What it saved (earlier build; indicative) |
|---|---|---|
| One cell atlas a level | every fixture's live textures are rows of one render target, drawn in one canvas pass a frame; a pass per fixture was the largest cost | the largest single saving; not restated, as its figure came from the capture that did not reproduce |
| Redraw only on new DMX | the patch reports only changed data; between changes only the lights update (a strobe is a function of time) | game thread; not restated, for the same reason |
| Light cutoff at 20 lux, radius never under 21.06 m | a light shades every pixel inside its radius; the cutoff alone gave a blinder lamp 15.3 m, but the engine fades a light to zero at its radius ((1-(d/R)^4)^2: 0.58 at 70 % of R), so that lamp gave 67 % of its light at 10 m and 39 % at 12 m. The radius now keeps 80 % of the light at a 12 m throw (group_reach in the vendored factory: 1.754 x the 12 m throw, 25 m at most) | the lights pass is 5.7 ms of the stage's 20.2 ms GPU on this build (2026-10-03); the 20-lux cutoff alone had taken it from 4.1 to 1.3 ms on an earlier build, before the 21 m floor and the redrawn housings: two builds, not a comparison |
| Lumen off on the demo stage | its light is direct, on a black deck; the stage's post-process volume sets GI to None and reflections to screen space (the project keeps Lumen for your maps) | about 3 ms of Lumen passes |
| No shadows from fixture lights | 156 shadow-casting spots would dominate everything. BP_StrobeFixture has a CastShadows setting, off by default: turn it on for a handful of hero fixtures and their beams stop at trusses, decks and bodies | — |
| Haze on, froxels 8 px | the look; r.VolumetricFog.GridPixelSize 16 is the cheaper setting | at 16 instead of 8, the fog pass about 2 ms less |
The overview (7 fixtures) runs at the same settings with Lumen on. Measure your own rig before you
assume any of these. The harness behind these figures (Tools/perf_run.sh) is the seller's bench
and does not ship in this download: it runs a map in -game with -csvCaptureFrames=900 -csvGpuStats and takes the medians from the CSV the engine writes under Saved/Profiling/CSV
(with the launcher engine on a Mac, ~/Library/Application Support/Epic/UnrealEngine/5.8/).
stat unit in a Play session gives a quicker reading.
The tooling — the Additional Files download
The project download holds no Python: Fab allows Python in a content project's zip only under
Content/Python/, which its rules forbid a pack to have. The pack's scripts, the vendored
cuesync_factory/ package they import and fixture_manifest.json are the listing's Additional
Files download, CueSyncStrobe_Tools_1_0_0.zip (its TOOLS.md says what is in it). Unzip it over
the project folder, the one holding CueSyncStrobe.uproject: it holds only Content/CueSyncStrobe/Docs/…,
so everything lands in this folder, beside the commands below. Nothing in the project needs it to
run. Without it, the fixtures, maps, materials and demo show work as described above and the
commands below are not there.
Standard-library Python. fixture_catalogue.py is every fixture as data; build_fixtures.py writes
the .gdtf files, the .mvr and fixture_manifest.json through cuesync_factory/ (the series'
shared writer, vendored by commit); the in-editor scripts build the Library, materials, meshes,
Blueprints, maps and show from them. Two verifiers:
python3 verify_pack.py— sixteen checks outside the editor (--selftestmakes each one fail once);verify_in_editor.py— fifteen checks inside it, seven of them live in Simulate: the textures read back cell by cell, each beam group's light following its own cells, a lamp's delivered candela, the light colour, the shutter blackout, the strobe rate, and the strobe's flash never being shorter than a frame (up to half the frame rate). Run it from Tools → Execute Python Script.
The in-editor scripts need the engine's Python Editor Script Plugin (PythonScriptPlugin), which
this project enables and which is a beta plugin in UE 5.8; nothing else in the pack needs it.
What runs with the download, and what is the seller's bench. Three things these scripts mention
do not ship in either archive: Tools/ (the seller's shell harnesses — perf_run.sh, author_base.sh,
verify_in_editor.sh, ue_run.sh), Content/CueSyncStrobeDev/ (the mesh template and the gallery
maps) and CueSyncPackFactory (the series' generator, which cuesync_factory/ is vendored from).
So:
verify_pack.py,verify_in_editor.pyandbuild_fixtures.pyneed nothing that is missing.verify_pack.py's strobe-tables check reads one more copy of the shutter ranges from the authoring commandlet's source when aCueSyncPackFactorycheckout sits beside the project; here there is none, so that one comparison is skipped and the rest of the check runs.build_meshes.pybuilds nothing and reports "no /Game/CueSyncStrobeDev/SM_MeshTemplate": it builds by duplicating that template, which stays on the seller's bench. The meshes ship built.build_fixture_blueprints.pyconfigures the fixtures on the shippedBP_StrobeFixture; re-authoring that base's graphs isTools/author_base.sh, which does not ship.- The scripts' docstrings name
Tools/wrappers for headless runs; with the Additional Files alone, run the in-editor scripts from Tools → Execute Python Script.
One pack per Python session. These scripts are this pack's own, but they share their module
names with the other CueSync fixture packs' — fixture_catalogue, build_fixtures, verify_pack,
verify_in_editor and the package cuesync_factory (CueSync Pixel ships the first three too). Each
script puts its own folder first on sys.path, so in one Python interpreter every script gets the
modules of whichever pack ran first. Run CueSync Moving Heads' build_fixtures.py and then this
pack's in the same interpreter, and this pack's reads Moving Heads' catalogue: it refuses to write
(every fixture is outside this pack's RDM range and name prefixes), and --reproduce reports 29
differences. In the other order, this pack's first, Moving Heads' script is given this pack's modules and
fails or reads the wrong pack. The Unreal editor keeps one
interpreter for the whole session, so in a project that holds two packs, restart the editor before
running the other pack's scripts. From a shell, each python3 run is a fresh interpreter and is
safe.