CanvasCraft

View Source

Elixir Hex

In-memory 2D rendering with a Skia backend (Rustler NIF). 100% declarative Scene DSL for charts and UI-like scenes.

Install

Add to mix.exs:

{:canvas_craft, "~> 0.2.0"}

This builds a small native library (Rust). You need a Rust toolchain (rustup recommended).

Requirements

  • Elixir >= 1.16, OTP >= 26
  • Rust toolchain (required to compile the NIF)
  • macOS or Linux (x86_64/arm64). Windows WSL works (experimental)

Features

  • Real in-memory WEBP export (no temp files)
  • Raw RGBA buffer access
  • Declarative DSL with named properties and per-element antialiasing
  • Primitives: clear, rect, circle; composites: panel, donut_segment, grid, scatter, progress_bar, line_chart, candle_chart

Preview

KitchenSink

Quickstart (Declarative DSL)

Minimal scene:

import CanvasCraft.Scene

render width: 128, height: 128, path: "out.webp" do
  rect x: 16, y: 16, w: 96, h: 96, color: {0, 128, 255, 255}
end

Skia backend is default. To get a binary instead of a file:

import CanvasCraft.Scene
{:ok, webp} = render width: 320, height: 240 do
  circle cx: 120, cy: 120, r: 80, color: {30, 180, 90, 255}
end
File.write!("circle.webp", webp)

DSL essentials

  • Colors: RGBA tuples {r,g,b,a} with 0..255 components
  • Antialiasing: aa 1 | 4 | 8 (can be set globally or per element, or scene-level via render aa: 8)

  • Coordinate system: pixels, origin at top-left
  • Named properties on all elements: rect x:, y:, w:, h:, color: etc.

Examples

  • examples/kitchensink – 1080p dashboard using the DSL (header + overview + system + analytics subplots)

Render KitchenSink (repo root):

mix run -e 'Code.compile_file("examples/kitchensink/lib/kitchensink.ex"); KitchenSink.render("kitchensink.webp", aa: 8)'
file kitchensink.webp

Or from the example folder:

cd examples/kitchensink
mix run -e 'Code.compile_file("lib/kitchensink.ex"); KitchenSink.render("output.webp", aa: 8)'
file output.webp

Tech details

  • Backend: Skia-like raster implemented as a Rust NIF (via Rustler)
  • Public API: CanvasCraft module (in-memory export, raw buffer)
  • DSL: CanvasCraft.Scene macro composes scenes and scopes AA
  • Encoding: WEBP (lossless); raw RGBA buffer for custom pipelines

Platforms & build

  • Compiles the NIF at dependency build time
  • Needs: Rust (rustup toolchain install stable), clang/LLVM on Linux

Troubleshooting

  • {:error, :backend_missing}: run from repo root using mix so Rustler loads the NIF. If needed: mix clean && mix compile.
  • NIF fails to load: ensure Rust toolchain is installed and on PATH.
  • Compile errors about clang: install build tools (xcode-select --install on macOS; build-essential on Ubuntu)

Contributing

See CONTRIBUTING.md. CI runs format, Credo, Dialyzer, tests, and non-blocking benches.

License

MIT