Strobe Lighting Effect
Light beams emanate from the corners, sweep across, and brighten where they cross - full-page or container-scoped, with named presets.
Domma.effects.strobe()
Renders a canvas of light beams that emanate from the corners of the page - or a container - and sweep or rotate across the space. Where beams overlap, their light adds together and brightens, just like real stage lighting. Pass null as the selector for a full-page lighting rig, or a selector to scope it to a single element. Everything is driven by a named preset, and any individual option overrides the preset's value. The effect returns a control object so you can pause, resume, stop, restart or destroy it at any time. Under prefers-reduced-motion the lighting is disabled entirely.
// Full-page club lighting
const lights = Domma.effects.strobe(null);
// A named preset
Domma.effects.strobe('#stage', { preset: 'police' });
// Preset + overrides
Domma.effects.strobe(null, { preset: 'club', flicker: false, colours: ['#ffffff'] });
lights.pause(); lights.resume(); lights.stop(); lights.destroy();
Options
| Option | Type | Default | Description |
|---|---|---|---|
preset |
string |
'club' |
Bundle of the options below. One of club, concert, police, searchlight, scanner, mood. Individual options override the preset. |
origins |
string[] |
preset | Which corners beams emit from - any of 'tl', 'tr', 'bl', 'br'. |
motion |
string |
preset | 'sweep' (fan back and forth) or 'rotate' (full circle). |
sweepArc |
number |
preset | Arc, in degrees, that beams fan through in sweep mode. |
speed |
number |
preset | Sweep / rotation speed multiplier. |
beamWidth |
number |
preset | Beam cone width in degrees. |
flicker |
boolean |
preset | Blink the beams on and off. |
hz |
number |
preset | Flicker rate (flashes per second). Values above 5 Hz log a console warning but are never clamped - keep it low for safety. |
colours |
string[] |
preset | Hex colour strings, assigned one per beam. |
intensity |
number |
preset | Overall brightness, 0-1. |
duration |
number | null |
null |
Auto-stop after this many milliseconds. null runs until stopped manually. |
zIndex |
number |
9999 |
Canvas stacking order. |
respectMotionPreference |
boolean |
true |
Honour the prefers-reduced-motion OS setting. When true and reduced motion is set, the lighting does not run. |
Presets
Each preset bundles a complete lighting look. Start from one, then override any option you like.
Domma.effects.strobe(null, { preset: 'club' })Domma.effects.strobe(null, { preset: 'concert' })Domma.effects.strobe(null, { preset: 'police' })Domma.effects.strobe(null, { preset: 'searchlight' })Domma.effects.strobe(null, { preset: 'scanner' })Domma.effects.strobe(null, { preset: 'mood' })<dt>club</dt>
<dd>Four-corner crisscross, multi-colour, fast flicker. <code>Domma.effects.strobe(null, { preset: 'club' })</code></dd>
<dt>concert</dt>
<dd>Two top beams fanning wide, rainbow, medium flicker. <code>Domma.effects.strobe(null, { preset: 'concert' })</code></dd>
<dt>police</dt>
<dd>Two beams red + blue, fast narrow sweep, hard flicker. <code>Domma.effects.strobe(null, { preset: 'police' })</code></dd>
<dt>searchlight</dt>
<dd>Bottom corners, smooth rotating white beams, no flicker. <code>Domma.effects.strobe(null, { preset: 'searchlight' })</code></dd>
<dt>scanner</dt>
<dd>Thin cyan beams, quick sweep, no flicker. <code>Domma.effects.strobe(null, { preset: 'scanner' })</code></dd>
<dt>mood</dt>
<dd>Slow wide pastel beams, smooth glow, no flicker. <code>Domma.effects.strobe(null, { preset: 'mood' })</code></dd>Demo: Preset Picker
Click a preset to select it, then press Start to light up the stage below. Nothing runs until you press Start.
<div class="preset-swatches" id="preset-picker"></div>
<div class="strobe-container" id="strobe-container-1">
<div class="strobe-container-label">
<div style="font-size: 2rem; margin-bottom: 0.5rem;">🔦</div>
<div>Pick a preset and press Start</div>
</div>
</div>
<span id="status-1" class="status-badge stopped"><span class="status-dot"></span><span class="status-text">Stopped</span></span>
<button type="button" class="btn btn-primary" id="start-1">Start</button>
<button type="button" class="btn btn-secondary" id="pause-1">Pause</button>
<button type="button" class="btn btn-secondary" id="resume-1">Resume</button>
<button type="button" class="btn btn-danger" id="stop-1">Stop</button>/**
* Demo: Preset Picker - from public/showcase/effects/strobe.
*/
export function mount(root) {
// ── Status badge helper ───────────────────────────────────────────────
function setStatus($badge, state) {
if (!$badge || !$badge.length) return;
const labels = { running: 'Running', paused: 'Paused', stopped: 'Stopped' };
$badge.attr('class', 'status-badge ' + state);
$badge.find('.status-dot')
.attr('class', 'status-dot' + (state === 'running' ? ' pulse-dot' : ''));
$badge.find('.status-text').text(labels[state] || state);
}
// ── Preset swatch colours (for the picker dots only) ─────────────────
const presetColours = {
club: ['#ff0066', '#00e5ff', '#ffee00', '#00ff88'],
concert: ['#ef4444', '#f97316', '#facc15', '#22c55e', '#0ea5e9', '#a855f7'],
police: ['#ff2222', '#2244ff'],
searchlight: ['#ffffff', '#e5e7eb'],
scanner: ['#22d3ee', '#06b6d4'],
mood: ['#fbcfe8', '#bae6fd', '#bbf7d0', '#ddd6fe']
};
let activePreset = 'club';
function renderPresetCard(name) {
const dots = presetColours[name].map(c => `<span style="background:${c}"></span>`).join('');
return `
<div class="preset-card${name === activePreset ? ' active' : ''}" data-preset="${name}">
<div class="preset-name">${name}</div>
<div class="preset-dots">${dots}</div>
</div>`;
}
// Note: pass {safe: false} because Domma's default html() sanitiser strips
// both inline `style` attributes and `data-*` attributes - we'd lose the
// swatch colours AND the data-preset hook the click handler reads.
const pickerMarkup = Object.keys(presetColours).map(renderPresetCard).join('');
$(root).find('#preset-picker').html(pickerMarkup, { safe: false });
$(root).find('#preset-picker').on('click', (e) => {
const card = e.target.closest('.preset-card');
if (!card) return;
$('#preset-picker .preset-card').removeClass('active');
card.classList.add('active');
activePreset = card.getAttribute('data-preset');
});
// ── Demo 1: Preset picker ────────────────────────────────────────────
let ctrl1 = null;
const badge1 = $(root).find('#status-1');
$(root).find('#start-1').on('click', () => {
if (ctrl1) ctrl1.destroy();
ctrl1 = Domma.effects.strobe('#strobe-container-1', {
preset: activePreset,
respectMotionPreference: false
});
setStatus(badge1, 'running');
});
$(root).find('#pause-1').on('click', () => { if (ctrl1) { ctrl1.pause(); setStatus(badge1, 'paused'); } });
$(root).find('#resume-1').on('click', () => { if (ctrl1) { ctrl1.resume(); setStatus(badge1, 'running'); } });
$(root).find('#stop-1').on('click', () => { if (ctrl1) { ctrl1.stop(); ctrl1 = null; setStatus(badge1, 'stopped'); } });
}Domma.effects.strobe('#strobe-container-1', {
preset: 'club' // the selected preset
});
Demo: Build Your Own
Configure the beams from scratch, then press Apply.
<label>Origins</label>
<label style="min-width:auto;"><input type="checkbox" class="opt-origin" value="tl" checked=""> Top-left</label>
<label style="min-width:auto;"><input type="checkbox" class="opt-origin" value="tr" checked=""> Top-right</label>
<label style="min-width:auto;"><input type="checkbox" class="opt-origin" value="bl" checked=""> Bottom-left</label>
<label style="min-width:auto;"><input type="checkbox" class="opt-origin" value="br" checked=""> Bottom-right</label>
<label>Motion</label>
<select class="form-control" id="opt-motion" style="max-width:180px;">
<option value="sweep" selected="">sweep</option>
<option value="rotate">rotate</option>
</select>
<label style="min-width:auto;"><input type="checkbox" id="opt-flicker" checked=""> Flicker</label>
<label>Sweep arc (°)</label>
<input type="range" id="opt-arc" min="20" max="180" step="5" value="100">
<span class="readout" id="val-arc">100</span>
<label>Flicker (Hz)</label>
<input type="range" id="opt-hz" min="1" max="15" step="1" value="10">
<span class="readout" id="val-hz">10</span>
<label>Speed</label>
<input type="range" id="opt-speed" min="0.2" max="3" step="0.1" value="1">
<span class="readout" id="val-speed">1</span>
<label>Beam width (°)</label>
<input type="range" id="opt-beam" min="4" max="40" step="1" value="16">
<span class="readout" id="val-beam">16</span>
<label>Intensity</label>
<input type="range" id="opt-intensity" min="0.2" max="1" step="0.05" value="0.7">
<span class="readout" id="val-intensity">0.7</span>
<label>Colours</label>
<input type="color" id="colour-a" value="#ffffff">
<input type="color" id="colour-b" value="#38bdf8">/**
* Demo: Build Your Own - from public/showcase/effects/strobe.
*/
export function mount(root) {
// ── Demo 2: Build your own ───────────────────────────────────────────
const readouts = ['arc', 'hz', 'speed', 'beam', 'intensity'];
readouts.forEach(name => {
$(`#opt-${name}`).on('input', (e) => { $(`#val-${name}`).text(e.target.value); });
});
let ctrl2 = null;
$(root).find('#apply-2').on('click', () => {
if (ctrl2) ctrl2.destroy();
const origins = $('.opt-origin:checked').get().map(c => c.value);
ctrl2 = Domma.effects.strobe('#strobe-container-2', {
origins: origins.length ? origins : ['tl'],
motion: $(root).find('#opt-motion').val(),
flicker: $(root).find('#opt-flicker').prop('checked'),
sweepArc: parseFloat($(root).find('#opt-arc').val()),
hz: parseFloat($(root).find('#opt-hz').val()),
speed: parseFloat($(root).find('#opt-speed').val()),
beamWidth: parseFloat($(root).find('#opt-beam').val()),
intensity: parseFloat($(root).find('#opt-intensity').val()),
colours: [$(root).find('#colour-a').val(), $(root).find('#colour-b').val()],
respectMotionPreference: false
});
});
}With flicker on, setting the Hz above 5 logs a console warning - the value is never clamped, so keep it low for safety.
<div class="strobe-container mt-3" id="strobe-container-2">
<div class="strobe-container-label">
<div>Press Apply to light the stage with your settings</div>
</div>
</div>
<button type="button" class="btn btn-primary" id="apply-2">Apply</button>
<button type="button" class="btn btn-danger" id="stop-2">Stop</button>/**
* Demo: Build Your Own - from public/showcase/effects/strobe.
*/
export function mount(root) {
let ctrl2 = null;
$(root).find('#stop-2').on('click', () => { if (ctrl2) { ctrl2.stop(); ctrl2 = null; } });
}Demo: Full-Page
Pass null as the selector to light the entire viewport. This affects the whole page - it uses the preset selected in the Preset Picker above (or club by default), so use the Stop button to end it. It never autoplays and never stops on its own here.
<span id="status-3" class="status-badge stopped"><span class="status-dot"></span><span class="status-text">Stopped</span></span>
<button type="button" class="btn btn-primary" id="start-3">Start Full-Page Lighting</button>
<button type="button" class="btn btn-danger" id="stop-3">Stop</button>/**
* Demo: Full-Page - from public/showcase/effects/strobe.
*/
export function mount(root) {
// ── Status badge helper ───────────────────────────────────────────────
function setStatus($badge, state) {
if (!$badge || !$badge.length) return;
const labels = { running: 'Running', paused: 'Paused', stopped: 'Stopped' };
$badge.attr('class', 'status-badge ' + state);
$badge.find('.status-dot')
.attr('class', 'status-dot' + (state === 'running' ? ' pulse-dot' : ''));
$badge.find('.status-text').text(labels[state] || state);
}
let activePreset = 'club';
// ── Demo 3: Full-page ────────────────────────────────────────────────
let ctrl3 = null;
const badge3 = $(root).find('#status-3');
$(root).find('#start-3').on('click', () => {
if (ctrl3) ctrl3.destroy();
ctrl3 = Domma.effects.strobe(null, {
preset: activePreset,
respectMotionPreference: false
});
setStatus(badge3, 'running');
});
$(root).find('#stop-3').on('click', () => { if (ctrl3) { ctrl3.stop(); ctrl3 = null; setStatus(badge3, 'stopped'); } });
// The full-page effect goes with the demo: stopped when it is reset or left.
return () => { if (ctrl3) ctrl3.destroy(); };
}const lights = Domma.effects.strobe(null, {
preset: 'club'
});
// ...later
lights.stop();