Playground
A software 3D renderer for voxel models: a rocket, a satellite and a ringed planet. It rasterises into a 128×128 buffer with a z-buffer, uses flat, stepped shading and a pixel outline, and is scaled up with crisp edges. Drag it or use the arrow keys to orbit. Every angle is an attribute, so Datastar signals can steer it.
Examples
Steered by sliders
Each slider is bound to a local signal, and each signal drives an attribute with data-attr. data-preserve-attr keeps those attributes when a server frame morphs the page.
<div data-signals="{_yaw: 30, _pitch: 15, _zoom: 1, _light: 45}" style="display: flex; flex-wrap: wrap; gap: 24px; align-items: center; inline-size: 100%">
<sb-voxel
data-attr:yaw="$_yaw" data-attr:pitch="$_pitch" data-attr:zoom="$_zoom" data-attr:light="$_light"
data-preserve-attr="yaw pitch zoom light"></sb-voxel>
<div style="display: grid; gap: 16px; flex: 1; min-inline-size: 14rem">
<sb-slider label="Yaw" min="-180" max="180" unit="°" data-bind:_yaw__prop.value></sb-slider>
<sb-slider label="Pitch" min="-89" max="89" unit="°" data-bind:_pitch__prop.value></sb-slider>
<sb-slider label="Zoom" min="0.25" max="2.5" step="0.05" data-bind:_zoom__prop.value></sb-slider>
<sb-slider label="Light" min="0" max="360" step="5" unit="°" data-bind:_light__prop.value></sb-slider>
</div>
</div>
Models
<sb-voxel model="rocket" spin="30" style="--sb-voxel-size: 10rem"></sb-voxel>
<sb-voxel model="satellite" spin="-20" pitch="25" style="--sb-voxel-size: 10rem"></sb-voxel>
<sb-voxel model="planet" spin="15" pitch="20" style="--sb-voxel-size: 10rem"></sb-voxel>
Report the orbit
After a drag, sb-orbit reports the effective angles.
<div data-signals:_orbit="'drag the satellite'">
<sb-voxel model="satellite" data-on:sb-orbit="$_orbit = 'yaw ' + Math.round(evt.detail.yaw) + '°, pitch ' + Math.round(evt.detail.pitch) + '°'"></sb-voxel>
<p data-text="$_orbit"></p>
</div>
Performance
It only paints when something changes: a prop, a drag, or each frame while spin is set. Offscreen instances pause, and with prefers-reduced-motion the model doesn't spin.
Accessibility
The canvas has role="img", and its label always states the model and current angles. It is focusable, and the arrow keys orbit (Shift for bigger steps).
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://starbase.zweiundeins.gmbh/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-voxel spin="40" pitch="18" style="--sb-voxel-size: 8.5rem"></sb-voxel>
<!-- In production, pin today's catalog instead of the latest: the browser then
refuses any file that changed. Add "integrity" to the import map above: the
hashes from https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/importmap.json and Datastar's, below. -->
<!--
<script type="importmap">
{ "imports": { "datastar": "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js" },
"integrity": { "https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.4/bundles/datastar-rocket.js": "sha384-vUxZojLrF1Ar3de5h7VINqhJXBgjyZS4U49pHvGa87kum6j5Xn6JRziuARNw5ELG", "…": "…from importmap.json" } }
</script>
<script type="module" src="https://starbase.zweiundeins.gmbh/c/@0a1ad1bf98a4/autoloader.js" integrity="sha384-IvSCUjEWPS+wwXunU+48x96KmyWDx/Y4LeCwCL8cWFqJ/zHmTsTOsSMrzTOcpW3D"></script>
-->
<!-- Or load just this component, pinned to this version: -->
<!-- <script type="module" src="https://starbase.zweiundeins.gmbh/c/voxel@0919f1f8658e/voxel.js" integrity="sha384-wrEanlfgKgo3QI4mIlwiG+JlZBEs/FntmNVDf64c9hz6jWP0wKhWPYIEYns1cJtL"></script> -->Size
Each file compressed on its own, the way it is served (gzip -9, brotli -11). Datastar and Rocket are shared by every component and not counted.
| File | Original | gzip | brotli |
|---|---|---|---|
voxel.js | 12.3 kB | 5.0 kB | 4.4 kB |
API reference
Props
| Attribute | Type | Default | Description |
|---|---|---|---|
model | "rocket" | "satellite" | "planet" | "rocket" | Which voxel model to show. |
yaw | number | 30 | Rotation around the vertical axis, in degrees. |
pitch | number | 15 | Tilt toward the viewer, in degrees. |
zoom | number | 1 | Scale factor. |
spin | number | 0 | Auto-rotation speed, in degrees per second. |
light | number | 45 | Light direction around the model, in degrees. |
Events
| Name | Description |
|---|---|
sb-orbit | After a drag. detail: { yaw, pitch } (effective angles). |