Twinkle Effect
Canvas-based twinkling stars animation. Works as a full-page ambient overlay or scoped to any container.
Domma.effects.twinkle()
Creates an ambient canvas-based starfield animation. Pass null (or omit the selector) for a full-page fixed overlay, or provide a selector to scope stars inside a specific container. Stars oscillate in brightness using a sinusoidal function and are drawn as 4-pointed shapes with a centre glow.
// Full-page overlay
const stars = Domma.effects.twinkle(null);
// Container-scoped stars
const stars = Domma.effects.twinkle('#hero-section', {
count: 80,
colour: 'rgba(200, 220, 255, 0.9)',
shape: 'circle'
});
// Control methods
stars.pause();
stars.resume();
stars.stop();
stars.restart();
stars.destroy();
Options
| Option | Type | Default | Description |
|---|---|---|---|
count |
number |
100 |
Number of stars to render |
minSize |
number |
1 |
Minimum star radius in pixels |
maxSize |
number |
3 |
Maximum star radius in pixels |
twinkleSpeed |
number |
0.003 |
Base oscillation speed per frame (higher = faster twinkling) |
colour |
string |
'rgba(255, 240, 200, 1)' |
Star colour (any CSS colour value) |
zIndex |
number |
1 |
Canvas stacking order |
shape |
'star' | 'circle' |
'star' |
'star' draws a 4-pointed shape with glow; 'circle' draws a soft dot |
respectMotionPreference |
boolean |
true |
Honour prefers-reduced-motion OS setting |
Control Methods
const stars = Domma.effects.twinkle('#section');
stars.pause(); // Pause rendering (stars freeze in place)
stars.resume(); // Resume from where it paused
stars.stop(); // Stop and cancel animation frame
stars.restart(); // Recreate stars and restart
stars.destroy(); // Stop + remove canvas from DOM
stars.isRunning(); // true when animating (not paused)
stars.isPaused(); // true when paused mid-animation
Demo: Container-Scoped Stars
Stars rendered inside a dark section element. The canvas is sized to the container and uses position: absolute so content above it remains interactive.
<div class="star-container" id="star-container-1">
<div class="star-container-label">
<div style="font-size: 2rem; margin-bottom: 0.5rem;">✦</div>
<div>Stars are rendered in this container</div>
<div style="font-size: 0.85rem; opacity: 0.7; margin-top: 0.3rem;">Click Start to initialise</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-secondary" id="restart-1">Restart</button>
<button type="button" class="btn btn-danger" id="destroy-1">Destroy</button>/**
* Demo: Container-Scoped Stars - from public/showcase/effects/twinkle.
*/
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);
}
// ── Demo 1: Container-scoped ──────────────────────────────────────────
let ctrl1 = null;
const badge1 = $(root).find('#status-1');
$(root).find('#start-1').on('click', () => {
if (ctrl1) ctrl1.destroy();
ctrl1 = Domma.effects.twinkle('#star-container-1', {
count: 80,
minSize: 1,
maxSize: 3,
colour: 'rgba(255, 240, 200, 0.9)',
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('#restart-1').on('click', () => {
if (ctrl1) { ctrl1.restart(); setStatus(badge1, 'running'); }
});
$(root).find('#destroy-1').on('click', () => {
if (ctrl1) { ctrl1.destroy(); ctrl1 = null; setStatus(badge1, 'stopped'); }
});
}const stars = Domma.effects.twinkle('#star-container', {
count: 80,
minSize: 1,
maxSize: 3,
colour: 'rgba(255, 240, 200, 0.9)',
respectMotionPreference: false
});
Demo: Full-Page Overlay
Pass null as the selector to create a position: fixed canvas overlay that covers the entire viewport. Stars sit behind page content via pointer-events: none. This is the mode used on the Domma splash page.
<span id="status-2" 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-2">Start Full-Page</button>
<button type="button" class="btn btn-secondary" id="pause-2">Pause</button>
<button type="button" class="btn btn-secondary" id="resume-2">Resume</button>
<button type="button" class="btn btn-danger" id="destroy-2">Destroy</button>/**
* Demo: Full-Page Overlay - from public/showcase/effects/twinkle.
*/
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);
}
// ── Demo 2: Full-page overlay ─────────────────────────────────────────
let ctrl2 = null;
const badge2 = $(root).find('#status-2');
$(root).find('#start-2').on('click', () => {
if (ctrl2) ctrl2.destroy();
ctrl2 = Domma.effects.twinkle(null, {
count: 120,
colour: 'rgba(255, 240, 200, 0.85)',
zIndex: 0,
respectMotionPreference: false
});
setStatus(badge2, 'running');
});
$(root).find('#pause-2').on('click', () => {
if (ctrl2) { ctrl2.pause(); setStatus(badge2, 'paused'); }
});
$(root).find('#resume-2').on('click', () => {
if (ctrl2) { ctrl2.resume(); setStatus(badge2, 'running'); }
});
$(root).find('#destroy-2').on('click', () => {
if (ctrl2) { ctrl2.destroy(); ctrl2 = null; setStatus(badge2, 'stopped'); }
});
}// null selector creates a full-page fixed canvas
const stars = Domma.effects.twinkle(null, {
count: 120,
colour: 'rgba(255, 240, 200, 0.85)',
zIndex: 0 // behind all page content
});
Demo: Customisation
Adjust options and click Apply to see the changes.
<label>Count</label>
<input type="range" id="opt-count" min="20" max="300" value="80">
<span id="val-count">80</span>
<label>Min Size</label>
<input type="range" id="opt-min-size" min="0.5" max="3" step="0.5" value="1">
<span id="val-min-size">1</span>
<label>Max Size</label>
<input type="range" id="opt-max-size" min="1" max="8" step="0.5" value="3">
<span id="val-max-size">3</span>
<label>Twinkle Speed</label>
<input type="range" id="opt-speed" min="0.001" max="0.02" step="0.001" value="0.003">
<span id="val-speed">0.003</span>
<strong style="font-size: 0.9rem;">Colour preset:</strong>
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem;">
<button type="button" class="btn btn-sm colour-btn active" data-colour="rgba(255, 240, 200, 1)">Warm White</button>
<button type="button" class="btn btn-sm colour-btn" data-colour="rgba(180, 210, 255, 1)">Cool Blue</button>
<button type="button" class="btn btn-sm colour-btn" data-colour="rgba(200, 255, 200, 1)">Soft Green</button>
<button type="button" class="btn btn-sm colour-btn" data-colour="rgba(255, 180, 200, 1)">Rose</button>
<button type="button" class="btn btn-sm colour-btn" data-colour="rgba(255, 200, 120, 1)">Amber</button>
</div>
<div class="star-container mt-4" id="star-container-3" style="min-height: 200px;">
<div class="star-container-label">
<div style="font-size: 0.9rem; opacity: 0.8;">Adjust sliders above and click Apply</div>
</div>
</div>
<button type="button" class="btn btn-primary" id="apply-3">Apply Options</button>
<button type="button" class="btn btn-danger" id="destroy-3">Destroy</button>/**
* Demo: Customisation - from public/showcase/effects/twinkle.
*/
export function mount(root) {
// ── Demo 3: Customisation ─────────────────────────────────────────────
let ctrl3 = null;
let selectedColour = 'rgba(255, 240, 200, 1)';
_.each(['count', 'min-size', 'max-size', 'speed'], id => {
const $el = $('#opt-' + id);
const $val = $('#val-' + id);
if ($el.length && $val.length) {
$el.on('input', () => $val.text($el.val()));
}
});
$('.colour-btn').on('click', function () {
selectedColour = $(this).attr('data-colour');
$('.colour-btn').removeClass('active');
$(this).addClass('active');
});
$(root).find('#apply-3').on('click', () => {
if (ctrl3) ctrl3.destroy();
ctrl3 = Domma.effects.twinkle('#star-container-3', {
count: parseInt($(root).find('#opt-count').val(), 10),
minSize: parseFloat($(root).find('#opt-min-size').val()),
maxSize: parseFloat($(root).find('#opt-max-size').val()),
twinkleSpeed: parseFloat($(root).find('#opt-speed').val()),
colour: selectedColour,
respectMotionPreference: false
});
});
$(root).find('#destroy-3').on('click', () => {
if (ctrl3) { ctrl3.destroy(); ctrl3 = null; }
});
}Demo: Shape Variants
The shape option controls how each star is drawn. 'star' renders a 4-pointed shape with a centre glow. 'circle' renders a soft filled dot.
shape: 'star' (default)
shape: 'circle'
<div>
<p class="text-center mb-2"><strong>shape: 'star'</strong> (default)</p>
<div class="star-container" id="shape-star" style="min-height: 160px;">
<div class="star-container-label" style="font-size: 0.85rem; opacity: 0.7;">4-pointed stars with glow</div>
</div>
</div>
<div>
<p class="text-center mb-2"><strong>shape: 'circle'</strong></p>
<div class="star-container" id="shape-circle" style="min-height: 160px;">
<div class="star-container-label" style="font-size: 0.85rem; opacity: 0.7;">Soft circular dots</div>
</div>
</div>
<button type="button" class="btn btn-primary" id="start-shapes">Start Both</button>
<button type="button" class="btn btn-danger" id="destroy-shapes">Destroy Both</button>/**
* Demo: Shape Variants - from public/showcase/effects/twinkle.
*/
export function mount(root) {
// ── Demo 4: Shape variants ────────────────────────────────────────────
let ctrlStar = null;
let ctrlCircle = null;
$(root).find('#start-shapes').on('click', () => {
if (ctrlStar) ctrlStar.destroy();
if (ctrlCircle) ctrlCircle.destroy();
ctrlStar = Domma.effects.twinkle('#shape-star', {
count: 60,
shape: 'star',
respectMotionPreference: false
});
ctrlCircle = Domma.effects.twinkle('#shape-circle', {
count: 60,
shape: 'circle',
respectMotionPreference: false
});
});
$(root).find('#destroy-shapes').on('click', () => {
if (ctrlStar) { ctrlStar.destroy(); ctrlStar = null; }
if (ctrlCircle) { ctrlCircle.destroy(); ctrlCircle = null; }
});
}// 4-pointed star (default)
Domma.effects.twinkle('#shape-star', { shape: 'star', count: 60 });
// Circular dot
Domma.effects.twinkle('#shape-circle', { shape: 'circle', count: 60 });