Space Adventure

An interactive 3D space canvas with drag-to-rotate and multi-input zoom controls.

Features: Drag to rotate the solar system in 3D. Pinch, double tap, or use keyboard/wheel to zoom.

Zoom Mode
Tip. Drag to rotate. Pinch or double tap to zoom.
In Desktop, use 'z' + '+/-' or 'wheel' to zoom.
Zoom
Signal Snapshot
SCALE 1.00
ROTATION 0, 0
INPUT -

Code

3D Rotation with Pan

import { pan } from "cereb";
import { rotate3d } from "cereb/operators";
// Pan gesture -> 3D rotation
pan(box, { threshold: 5 })
.pipe(
rotate3d({
sensitivityX: 0.5,
sensitivityY: 0.5,
}),
)
.on((signal) => {
const [drx, dry, drz] = signal.value.rotation;
rotationManager.addDelta(drx, dry, drz);
});

Pinch Zoom with Delta

import { pinch } from "cereb";
import { zoom as createZoom, extend } from "cereb/operators";
const PINCH_SENSITIVITY = 0.4;
// Pinch zoom - uses delta-based zoom operator
pinch(box, { threshold: 10 })
.pipe(
extend((signal) => ({
ratio: 1 + (signal.value.ratio - 1) * PINCH_SENSITIVITY,
})),
createZoom(),
)
.on((signal) => {
// Accumulate delta and clamp
const newScale = clamp(
zoomManager.getScale() + signal.value.scale,
MIN_SCALE,
MAX_SCALE
);
zoomManager.setScale(newScale);
});

Keyboard & Wheel Zoom

import { wheel, keydown, keyheld } from "cereb";
import { when, spy } from "cereb/operators";
const zoomMode$ = keyheld(window, { code: "KeyZ" })
.pipe(extend((signal) => ({ opened: signal.value.held })));
// 'z' + '+/-' keyboard zoom
keydown(window, { code: ["Equal", "Minus"] })
.pipe(
when(zoomMode$),
spy((signal) => signal.value.originalEvent.preventDefault()),
)
.on((signal) => {
const currentScale = zoomManager.getScale();
const multiplier = signal.value.code === "Equal" ? 1.2 : 1 / 1.2;
const targetScale = clamp(currentScale * multiplier, MIN_SCALE, MAX_SCALE);
zoomManager.setScale(targetScale);
});
// 'z' + wheel zoom
wheel(box, { passive: false })
.pipe(
when(zoomMode$),
spy((signal) => signal.value.originalEvent.preventDefault()),
)
.on((signal) => {
const currentScale = zoomManager.getScale();
const multiplier = Math.exp(-signal.value.deltaY * 0.005);
const targetScale = clamp(currentScale * multiplier, MIN_SCALE, MAX_SCALE);
zoomManager.setScale(targetScale);
});