MCPcopy Index your code
hub / github.com/andrewcourtice/ripl

github.com/andrewcourtice/ripl @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
1,843 symbols 5,027 edges 294 files 99 documented · 5%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Ripl

Ripl (pronounced "ripple") is a library that provides a unified API for 2D graphics rendering (Canvas & SVG) in the browser, with a focus on high performance and interactive data visualization. It also includes an experimental 3D rendering package.

Working with the canvas API can be notoriously difficult as it is designed to be very low-level. Alternatively, working with SVG is rather straightforward but not without its flaws. Because these paradigms differ widely in their implementations developers often have to choose one or the other at the outset of a project. Ripl alleviates the issue by exposing a unified API and mimicking the DOM/CSSOM in as many ways as possible to make it simple for developers to interact with. Switching between Canvas and SVG is as simple as changing one line of code.

[!IMPORTANT] Ripl is currently in beta.

Spline Polyline Spline Curve 3D Sphere Sphere Rendering Multi-Line Graph Line Chart Analysis
Bubble Scatter Scatter Plot Candlestick Chart Stock Market Chart Jet Engine Model 3D Jet Engine

Features

  • Unified rendering API across Canvas, SVG, and experimental 3D contexts
  • Grouping and property inheritance — CSS-like cascading of visual properties through the element tree
  • Scene and renderer management — hoisted scenegraph with O(n) rendering and an automatic requestAnimationFrame loop
  • DOM-like event system — event bubbling, delegation, stop propagation, and disposable subscriptions
  • CSS-like element queryinggetElementById, getElementsByType, getElementsByClass, query, queryAll with selector syntax
  • Bounding box detection via getBoundingBox on all shape elements
  • Transforms — translate, scale, rotation, and transform-origin on every element
  • Clipping — path-based clipping via Shape2D
  • Gradient support — CSS gradient parsing and serialisation (linear, radial, conic)
  • Automatic interpolation for numbers, colors (RGB, hex, HSL), dates, gradients, paths, strings, and rotation values
  • High performance animation — cancellable Task-based transitions with CSS-like keyframe support and custom interpolators
  • 11 scale types — continuous, discrete, band, diverging, logarithmic, power, quantile, quantize, threshold, time (inspired by D3)
  • 18 pre-built chart types via @ripl/charts
  • Built-in shape primitives — arc, circle, rect, line, polyline, polygon, ellipse, text, path, image
  • 3D primitives (experimental) — cube, sphere, cylinder, cone, plane, torus
  • Easing library — linear, quad, cubic, quart, quint (in/out/inOut variants)
  • Color utilities — parsing, serialisation, and color scales
  • Math & geometry — degree/radian conversion, point operations, border radius normalisation, polygon extrapolation
  • Renderer debug overlay — FPS counter, element count, bounding box visualisation
  • Completely modular and tree-shakable — only ship the features you use
  • Strictly typed in TypeScript
  • Zero runtime dependencies

Packages

Package Description
@ripl/web Main entry point for browser usage — re-exports core + canvas context with browser platform bindings
@ripl/core Core rendering — elements, scene, renderer, animation, scales, math, color, interpolation, gradients, tasks
@ripl/canvas Canvas 2D rendering context
@ripl/svg SVG rendering context
@ripl/charts Pre-built chart components with axes, legends, tooltips, crosshairs, and grids
@ripl/3d 3D rendering context with camera, shading, and primitive shapes (experimental)
@ripl/webgpu WebGPU-accelerated 3D rendering context with hardware depth testing and WGSL shaders
@ripl/terminal Terminal rendering context — braille-character output with ANSI truecolor
@ripl/node Node.js runtime bindings — configures the platform factory for headless environments
@ripl/dom DOM utilities used internally by browser contexts
@ripl/utilities Shared typed utility functions — type guards, collection helpers, DOM helpers

The project is structured as a Yarn 4 monorepo:

packages/
├── core/         # Core rendering library
├── canvas/       # Canvas 2D rendering context
├── svg/          # SVG context implementation
├── charts/       # Pre-built chart components
├── 3d/           # 3D rendering (experimental)
├── webgpu/       # WebGPU 3D rendering context
├── terminal/     # Terminal rendering context
├── node/         # Node.js runtime bindings
├── web/          # Main browser entry point
├── dom/          # DOM utilities
├── utilities/    # Shared typed utility functions
└── test-utils/   # Test utilities
app/              # Documentation site (VitePress) with live demos

Usage

The following is a tour of Ripl's features starting from the most basic and progressively building towards more advanced concepts.

Render a Basic Element

import {
    createCircle,
    createContext,
} from '@ripl/web';

// Create a canvas context bound to a DOM element
const context = createContext('.mount-element');

// Create an element
const circle = createCircle({
    fill: 'rgb(30, 105, 120)',
    lineWidth: 4,
    cx: context.width / 2,
    cy: context.height / 2,
    radius: context.width / 3,
});

// Render the element to the context
circle.render(context);

Built-in 2D shape primitives: arc, circle, rect, line, polyline, polygon, ellipse, text, path, image.

Modify Element Properties

To modify an element simply change any of its properties and re-render it.

circle.fill = '#FF0000';
circle.cx = context.width / 3;
circle.cy = context.height / 3;
circle.render(context);

Switch Contexts (Canvas / SVG)

To render the same element to SVG, replace the createContext import from @ripl/web with @ripl/svg:

import {
    createContext,
} from '@ripl/svg';

import {
    createCircle,
} from '@ripl/web';

const context = createContext('.mount-element');
const circle = createCircle({ /* same options */ });
circle.render(context);

Grouping and Inheritance

Render multiple elements in groups with inherited properties (like CSS) and event bubbling (like the DOM):

import {
    createCircle,
    createContext,
    createGroup,
    createRect,
} from '@ripl/web';

const context = createContext('.mount-element');

const circle = createCircle({
    cx: context.width / 2,
    cy: context.height / 2,
    radius: context.width / 3,
});

const rect = createRect({
    x: context.width / 2,
    y: context.height / 2,
    width: context.width / 5,
    height: context.height / 5,
});

// Both children inherit fill and lineWidth from the group
const group = createGroup({
    fill: 'rgb(30, 105, 120)',
    lineWidth: 4,
    children: [circle, rect],
});

group.render(context);

Querying Elements

Elements can be queried using common DOM methods or CSS-like selectors:

const circles = parentGroup.getElementsByType('circle');
const shapes = parentGroup.queryAll('.shape');
const first = parentGroup.query('#child-group > .shape');

Supported selector features:

circle                                   /* type */
#element-id                              /* id */
.element-class                           /* class */
circle[radius="5"]                       /* attribute */
.group-class circle                      /* descendant */
.group-class > circle                    /* direct child */
.group-class rect + circle.circle-class  /* adjacent sibling */

Scene and Renderer

A Scene is the top-level group bound to a rendering context. A Renderer drives the animation loop via requestAnimationFrame.

import {
    createCircle,
    createGroup,
    createRect,
    createRenderer,
    createScene,
} from '@ripl/web';

const circle = createCircle({
    fill: 'rgb(30, 105, 120)',
    cx: 100,
    cy: 100,
    radius: 40,
});

const rect = createRect({
    fill: 'rgb(30, 105, 120)',
    x: 200,
    y: 80,
    width: 60,
    height: 60,
});

// Scene takes a target (selector, element, or context) and options
const scene = createScene('.mount-element', {
    children: [circle, rect],
});

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

// Listen for events
circle.on('click', event => console.log(event));

Animation

The renderer provides transition-based animation. Transitions are cancellable Task instances (extending Promise with AbortController integration).

import {
    easeOutCubic,
} from '@ripl/web';

// Animate a single element
await renderer.transition(circle, {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        fill: '#FF0000',
        cx: 200,
        cy: 200,
        radius: 60,
    },
});

// Animate multiple elements (or a whole group/scene)
await renderer.transition([circle, rect], {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        fill: '#FF0000',
    },
});

Keyframes

// Implicit keyframe offsets
await renderer.transition(circle, {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        fill: [
            '#FF0000', // offset ~0.33
            '#00FF00', // offset ~0.66
            '#0000FF', // offset 1
        ],
    },
});

// Explicit keyframe offsets
await renderer.transition(circle, {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        fill: [
            { value: '#FF0000',
                offset: 0.25 },
            { value: '#0000FF',
                offset: 0.8 },
        ],
    },
});

// Custom interpolator function
await renderer.transition(circle, {
    duration: 1000,
    ease: easeOutCubic,
    state: {
        radius: t => t * 100, // 0 <= t <= 1
    },
});

Transforms

Every element supports CSS-like transforms:

const rect = createRect({
    x: 100,
    y: 100,
    width: 80,
    height: 80,
    rotation: '45deg', // or radians as a number
    transformOriginX: '50%', // or pixels as a number
    transformOriginY: '50%',
    translateX: 20,
    translateY: 10,
    transformScaleX: 1.5,
    transformScaleY: 1.5,
});

Transforms can also be animated via renderer.transition.

Charts

@ripl/charts provides 18 ready-to-use, animated chart types. Each chart supports tooltips, legends, crosshairs, grids, axes, and data update animations out of the box.

Chart Factory
Area createAreaChart
Bar createBarChart
Chord createChordChart
Funnel createFunnelChart
Gantt createGanttChart
Gauge createGaugeChart
Heatmap createHeatmapChart
Line createLineChart
Pie / Donut createPieChart
Polar Area createPolarAreaChart
Radar createRadarChart
Realtime createRealtimeChart
Sankey createSankeyChart
Scatter createScatterChart
Stock (OHLC) createStockChart
Sunburst createSunburstChart
Treemap createTreemapChart
Trend (Bar + Line) createTrendChart

Charts are constructed by passing a target element and an options object:

import {
    createBarChart,
} from '@ripl/charts';

const chart = createBarChart('.mount-element', {
    data: [
        { category: 'A',
            value: 30 },
        { category: 'B',
            value: 70 },
        { category: 'C',
            value: 45 },
    ],
    keyBy: 'category',
    series: [
        { label: 'Values',
            valueBy: item => item.value },
    ],
});

// Update with new data
chart.update({
    data: [
        { category: 'A',
            value: 50 },
        { category: 'B',
            value: 20 },
        { category: 'C',
            value: 80 },
    ],
});

Reusable chart components: ChartXAxis, ChartYAxis, Grid, Legend, Tooltip, Crosshair.

Scales

Ripl provides 11 scale types for mapping data between domains and ranges, inspired by D3. All scales expose inverse, ticks, and includes methods.

import {
    scaleBand,
    scaleContinuous,
    scaleDiscrete,
    scaleDiverging,
    scaleLogarithmic,
    scalePower,
    scaleQuantile,
    scaleQuantize,
    scaleThreshold,
    scaleTime,
} from '@ripl/web';

Continuous (Linear)

const scale = scaleContinuous([0, 25], [-100, 100]);
scale(10); // -20
scale.inverse(-20); // 10

// With clamping
const clamped = scaleContinuous([0, 25], [-100, 100], { clamp: true });
clamped(30); // 100

Discrete

const scale = scaleDiscrete(['a', 'b', 'c'], [0, 50]);
scale('b'); // 25

Band

const scale = scaleBand(['A', 'B', 'C'], [0, 300], {
    innerPadding: 0.1,
    outerPadding: 0.05,
});
scale('B'); // position of band B
scale.bandwidth; // width of each band

Additional Scales

  • scaleDiverging — maps values below and above a midpoint to separate sub-ranges
  • scaleLogarithmic — logarithmic mapping with configurable base
  • scalePower — polynomial mapp

Extension points exported contracts — how you extend this code

SVGContextElement (Interface)
(no doc) [4 implementers]
packages/svg/src/index.ts
Rasterizer (Interface)
(no doc) [2 implementers]
packages/terminal/src/rasterizer.ts
ContextElement (Interface)
(no doc) [2 implementers]
packages/core/src/context/types.ts
BenchElement (Interface)
(no doc)
packages/dom/test/reconcile.bench.ts
RescaleResult (Interface)
(no doc)
packages/canvas/src/helpers.ts
CameraInteractionConfig (Interface)
(no doc)
packages/3d/src/camera.ts
ArrayJoin (Interface)
(no doc)
packages/utilities/src/collection.ts
PipelineState (Interface)
(no doc)
packages/webgpu/src/pipeline.ts

Core symbols most depended-on inside this repo

push
called by 320
packages/charts/src/charts/realtime.ts
getStateValue
called by 253
packages/core/src/core/element.ts
setStateValue
called by 168
packages/3d/src/core/shape.ts
getAnimationDuration
called by 145
packages/charts/src/core/chart.ts
transition
called by 144
packages/core/src/core/renderer.ts
destroy
called by 133
packages/core/src/core/scene.ts
scale
called by 113
packages/core/src/context/context.ts
add
called by 99
packages/core/src/core/group.ts

Shape

Method 862
Function 598
Class 198
Interface 185

Languages

TypeScript100%

Modules by API surface

packages/svg/src/index.ts71 symbols
packages/core/src/context/context.ts61 symbols
packages/3d/src/context.ts60 symbols
packages/canvas/src/context.ts57 symbols
packages/core/src/core/element.ts47 symbols
packages/3d/src/core/shape.ts30 symbols
packages/core/src/elements/polyline.ts28 symbols
packages/3d/src/camera.ts26 symbols
packages/core/src/core/renderer.ts24 symbols
packages/core/src/core/group.ts24 symbols
packages/core/src/core/factory.ts24 symbols
app/src/demos/combustion-engine/elements/crankshaft.ts24 symbols

For agents

$ claude mcp add ripl \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact