Playground
Clouds of gas and a few twinkling stars, rendered by a fragment shader. The shader draws domain-warped noise at low resolution and posterizes it with an ordered dither, so it looks like pixel art. Colours come from theme tokens, and the nebula leans toward the pointer.
Examples
Controls
<div data-signals="{_speed: 1, _density: 0.5, _pixel: 4}" style="display: grid; gap: 16px; inline-size: 100%">
<sb-nebula data-attr:speed="$_speed" data-attr:density="$_density" data-attr:pixel="$_pixel" data-preserve-attr="speed density pixel"></sb-nebula>
<div style="display: flex; gap: 24px; flex-wrap: wrap">
<sb-slider label="Speed" max="5" step="0.1" style="flex: 1; min-inline-size: 10rem" data-bind:_speed__prop.value></sb-slider>
<sb-slider label="Density" max="1" step="0.05" style="flex: 1; min-inline-size: 10rem" data-bind:_density__prop.value></sb-slider>
<sb-slider label="Pixel size" min="1" max="16" style="flex: 1; min-inline-size: 10rem" data-bind:_pixel__prop.value></sb-slider>
</div>
</div>
Palettes
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 12px; inline-size: 100%">
<sb-nebula palette="violet" style="--sb-nebula-height: 7rem"></sb-nebula>
<sb-nebula palette="aurora" seed="7" style="--sb-nebula-height: 7rem"></sb-nebula>
<sb-nebula palette="ember" seed="3" density="0.7" style="--sb-nebula-height: 7rem"></sb-nebula>
<sb-nebula palette="mono" seed="12" style="--sb-nebula-height: 7rem"></sb-nebula>
</div>
Smooth
levels="0" turns off the posterizing, and pixel="1" renders at full resolution.
<sb-nebula levels="0" pixel="2" speed="0.6" style="--sb-nebula-height: 9rem"></sb-nebula>
Performance
One full-screen triangle and a single shader, rendered at a quarter of the CSS resolution by default (pixel="4"). It stops animating offscreen and when speed is 0. It frees its WebGL context when removed, and recovers when the browser takes the context away. Without WebGL it shows a still gradient in the same colours. Set the height with --sb-nebula-height.
Accessibility
It is decorative motion, labelled "Animated nebula" (change it with label). Under prefers-reduced-motion it shows one still frame and ignores the pointer.
Installation
Add Datastar with Rocket and the Starbase autoloader once per page, then use the tag. The autoloader imports each component the first time its tag appears, including tags added later by a Datastar morph.
<!-- Once per page: Datastar with Rocket, and the Starbase autoloader.
It loads every <sb-…> component the first time its tag appears. -->
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" } }
</script>
<script type="module" src="https://rocket.libretto.ch/c/autoloader.js"></script>
<!-- Optional, no flash of undefined elements: class="sb-cloak" on <html>, and -->
<style>.sb-cloak :not(:defined) { visibility: hidden }</style>
<sb-nebula speed="1.2" density="0.6" style="--sb-nebula-height: 7.5rem"></sb-nebula>
<!-- Or skip the autoloader and load just this component: -->
<!-- <script type="module" src="https://rocket.libretto.ch/c/nebula/nebula.js"></script> -->API reference
Props
| Attribute | Type | Default | Description |
|---|---|---|---|
speed | number | 1 | Drift speed (0 is still). |
density | number | 0.5 | How much of the sky the gas covers, 0 to 1. |
palette | "violet" | "aurora" | "ember" | "mono" | "violet" | Colour scheme (from theme tokens). |
pixel | number | 4 | Size of one pixel, in CSS pixels. |
levels | number | 6 | Colour levels per channel, dithered (0 for smooth). |
stars | boolean | true | Draw twinkling stars. |
seed | number | 0 | Picks a different nebula. |
parallax | boolean | true | Shift with the pointer. |
label | string | "Animated nebula" | Accessible label. |