hephaestus 0.2.0

Backend-agnostic 2D scene renderer for data visualization.
Documentation

hephaestus

A backend-agnostic 2D scene renderer for data visualization, written in Rust.

hephaestus ships two API levels in one crate. The scene API (SceneBuilder + primitives + layout) is a backend-neutral drawing surface. The plot API (plot::* — geoms, scales, themes, and the PlotComposition orchestrator) is a grammar-of-graphics layer built on top of it. Use whichever level fits; they are layered, not alternatives.

The public surface is deliberately the intersection of what Vello and Blend2D natively support, so drawing code runs unchanged as backends are added. The initial backend is Vello (GPU compute via wgpu); Blend2D (CPU raster), SVG, and PDF are planned.

Performance on dense, interactively-updated plots is the design driver. WASM is supported but is not the primary target.

Install

cargo add hephaestus

Quick start — the plot API

use hephaestus::backend::vello::VelloRenderer;
use hephaestus::color::{rgb8, Color};
use hephaestus::composition::{grid, Patch};
use hephaestus::geometry::Size;
use hephaestus::plot::chrome::axis::{Axis, AxisPlacement};
use hephaestus::plot::{scale, Plot, PlotComposition, PointGeom};
use hephaestus::scales::chrome::AxisSide;
use hephaestus::scene::SceneBuilder;
use hephaestus::Renderer;

let comp = || grid(1, 1, vec![Patch::new("main").into()]);
let xs: Vec<f64> = (0..60).map(|i| i as f64).collect();
let ys: Vec<f64> = xs.iter().map(|x| (x * 0.1).sin()).collect();

let mut plot = Plot::new(&comp(), "main").bind("x", "t").bind("y", "value");
plot.add_geom(
    PointGeom::builder()
        .set("x", xs)
        .set("y", ys)
        .set("fill", rgb8(220, 90, 70))
        .set("size", 4.0_f64)
        .build(),
);
plot.set_title("A sine wave");
plot.add_axis(Axis::rail("t", AxisPlacement::Cartesian(AxisSide::Bottom)));
plot.add_axis(Axis::rail("value", AxisPlacement::Cartesian(AxisSide::Left)));

let mut view = PlotComposition::new(&comp())
    .add_scale("t", scale::continuous(0.0..=60.0))
    .add_scale("value", scale::continuous(-1.0..=1.0))
    .with_plot(plot);

let (w, h) = (800u32, 500u32);
let mut renderer = VelloRenderer::new().expect("vello init");
{
    let scene = renderer.scene();
    scene.clear();
    view.render(scene, Size::new(w as f64, h as f64), 96.0);
}
let mut pixels = vec![0u8; (w * h * 4) as usize];
let bg: Color = rgb8(255, 255, 255);
renderer.render_to_buffer(w, h, bg, &mut pixels).expect("render");

Scales are shared by name: two plots that both bind("x", "t") resolve to the same Scale, so view.update_scale("t", ...) moves both panels from one call. Compositions nest — beside, stack, and friends build patchwork layouts whose axes and titles align across panels.

Quick start — the scene API

use hephaestus::backend::vello::VelloRenderer;
use hephaestus::color::rgb8;
use hephaestus::{Affine, Brush, FillRule, PickId, Rect, Renderer, SceneBuilder};
use hephaestus::geometry::Shape;

let mut renderer = VelloRenderer::new().expect("vello init");
{
    let scene = renderer.scene();
    let path = Rect::new(40.0, 40.0, 240.0, 200.0).to_path(0.1);
    let brush: Brush = rgb8(60, 120, 200).into();
    scene.fill(FillRule::NonZero, Affine::IDENTITY, &brush, None, &path, PickId::Skip);
}

The renderer produces RGBA8 buffers for file output. The optional window feature presents those frames live instead — an OS window, resize, and pointer events wired to picking. Animation scheduling stays the host's problem by design.

What's in the box

  • Geoms — point, line, segment, rect, ellipse, polygon, wedge, ribbon, B-spline, ribbon-B-spline, geometry (WKT / WKB / GeoJSON), and three text geoms (mark, fit-to-box, along-path).
  • Scales — continuous, discrete, binned, temporal, identity; log / sqrt / custom transforms; automatic and pinned breaks for both majors and minors; locale-aware label formatting.
  • Projections — cartesian and polar, plus a CustomProjection hook. Non-linear projections densify edges so straight lines in data space curve correctly.
  • Chrome — axes, legends, colorbars, facet strips, titles, captions, all theme-driven.
  • Themes — every chrome element is a themed element; no hardcoded constants in the render path.
  • Rich text — a marquee-flavoured markdown subset with inline styling, block borders, and backgrounds.
  • Picking — opt-in per renderer; emits pixel ids for hit-testing without a CPU post-pass.
  • Live window — optional window feature: presents frames on screen, re-lays-out on resize, and reports the pick id under the cursor.

Features

Feature Default What it does
vello GPU rasterizer via wgpu.
png PNG writer. Lossless, alpha preserved.
jpeg JPEG writer. Lossy, and the format has no alpha channel, so the buffer is composited onto a background color.
tiff TIFF writer. Lossless, alpha preserved, choice of compressor.
webp WebP writer. Lossless, alpha preserved, and smaller than PNG on most plots.
window Live window presentation: OS window, wgpu surface, event loop with resize and pointer events (winit). Requires vello.
google-fonts Fetch named Google Fonts families on demand. Network call on cache miss; cache hits are offline.
chrono / time / jiff From impls between the temporal newtypes and the matching datetime library. Pick whichever your code already uses.
geom-wkt / geom-wkb / geom-geojson Parsers for scales::Geometry. Hand-rolled and dependency-free, so toggling them only changes what constructors compile.
blend2d / svg / pdf Placeholders. Wired through cargo so dependent crates can name them; no backend code behind them yet.

The core types and traits build with --no-default-features, pulling in no wgpu, so downstream crates can target SceneBuilder without GPU dependencies.

The four writer features are encoders over the RGBA8 buffer a renderer already produces, so each one costs only its own encoder — all four are pure Rust and build for wasm. They live together in hephaestus::image.

Examples

Around 60 runnable examples live in examples/, each writing its output next to itself:

cargo run --example hello         # scene API sanity check
cargo run --example point         # plot API, shared scales across panels
cargo run --example polar         # polar projection
cargo run --example legends       # legend and colorbar variants
cargo run --example theme_dark    # theming

cargo run --example image_formats --features jpeg,tiff,webp  # all four raster writers

One example opens a window instead of writing a file:

cargo run --example window --features window   # live plot: resize + hover picking

Status

Pre-1.0 and pre-stability: the API is expected to change as the remaining backends land. kurbo, peniko, and wgpu types appear in the public surface, so their major versions are part of this crate's semver contract.

License

MIT — see LICENSE.md.