Skip to content

Renderer

A Renderer provides an automatic render loop for a Scene, powered by requestAnimationFrame. It continuously re-renders the scene each frame, enabling smooth animations and interactive effects. The renderer also provides a transition() method for animating element properties.

The Renderer is what brings your scene to life. Without it, you'd need to manually call scene.render() every time something changes. With a Renderer, you describe what should change (via transitions) and the renderer handles when and how — including easing, chaining, staggering, and automatic start/stop to conserve resources when idle.

Demo

Click "Animate" to run a transition. Click "Stagger" to see per-element staggered animation.

Creating a Renderer

ts
import {
    createRenderer,
    createScene,
} from '@ripl/web';

const scene = createScene('.my-container', {
    children: [circle, rect],
});

const renderer = createRenderer(scene);

Options

The renderer accepts autoStart (default true) and autoStop (default true). With autoStop, the renderer pauses when idle and restarts when needed:

ts
const renderer = createRenderer(scene, {
    autoStart: false,
    autoStop: false,
});

Debug Mode

The renderer includes built-in debug overlays to help you inspect and profile your scene. Pass the debug option to createRenderer to enable them.

Setting debug: true enables all overlays at once:

ts
const renderer = createRenderer(scene, {
    debug: true,
});

For granular control, pass an object with the specific overlays you want:

ts
const renderer = createRenderer(scene, {
    debug: {
        fps: true, // frames-per-second counter
        elementCount: true, // number of elements in the scene buffer
        boundingBoxes: true, // red outline around every element's bounding box
    },
});
OptionDescription
fpsDisplays a smoothed frames-per-second counter in an overlay badge
elementCountShows the total number of elements rendered each frame
boundingBoxesDraws a red stroke rectangle around every element's bounding box

When fps or elementCount is enabled, a semi-transparent overlay badge appears in the top-left corner of the scene. boundingBoxes renders directly onto the canvas after each element, making it easy to verify hit areas and layout.

Demo

Toggle the debug overlays below to see them in action.

Render Loop

When running, the renderer executes a tick on every animation frame:

  1. Clears the context
  2. Advances all active transitions
  3. Renders every element in the scene buffer
  4. Requests the next frame

With autoStop enabled (default), the renderer automatically stops when there are no active transitions and the mouse leaves the scene. It restarts when the mouse re-enters or a new transition is created.

Transitions

The transition() method smoothly animates element properties from their current values to new target values. It returns a Promise-like Transition that resolves when the animation completes.

Basic Transition

ts
await renderer.transition(circle, {
    duration: 1000,
    state: {
        radius: 100,
        fill: '#ff006e',
    },
});

Easing Functions

Ripl provides a full set of easing functions:

ts
import {
    easeInCubic,
    easeInOutCubic, easeInOutQuad, easeInOutQuart,
    easeInOutQuint, easeInQuad, easeInQuart,
    easeInQuint, easeLinear, easeOutCubic,
    easeOutQuad, easeOutQuart, easeOutQuint,
} from '@ripl/web';

await renderer.transition(circle, {
    duration: 800,
    ease: easeOutCubic,
    state: { radius: 100 },
});

Chaining Transitions

Because transition() returns a promise, you can chain animations sequentially:

ts
await renderer.transition(circle, {
    duration: 500,
    ease: easeOutCubic,
    state: { radius: 100 },
});

await renderer.transition(circle, {
    duration: 500,
    ease: easeOutCubic,
    state: { radius: 50 },
});

Animating Multiple Elements

Pass an array of elements or a group to animate them all with the same options:

ts
await renderer.transition([circle, rect], {
    duration: 800,
    ease: easeOutCubic,
    state: { fill: '#ff006e' },
});

Per-Element Options

Pass a function instead of an options object to customize the transition per element. This is useful for staggered animations:

ts
await renderer.transition(group, (element, index, total) => ({
    duration: 500,
    delay: index * 100, // stagger by 100ms
    ease: easeOutCubic,
    state: { fill: '#ff006e' },
}));

Manual Control

start()

Manually start the render loop:

ts
renderer.start();

stop()

Manually stop the render loop. Clears all active transitions:

ts
renderer.stop();

Events

The renderer emits start and stop events to track the render loop lifecycle:

ts
renderer.on('start', (event) => {
    console.log('Renderer started at', event.data.startTime);
});

Cleanup

The renderer automatically destroys itself when the scene is destroyed. You can also destroy it manually:

ts
renderer.destroy(); // stops the loop and cleans up

NOTE

For the full list of Renderer properties, methods, and events, see the Renderer API Reference.