CanvasCraft
View SourceIn-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.1"}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
- Parallel rendering using dirty schedulers - multiple canvases render concurrently without blocking the VM
- 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

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}
endSkia 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 viarender 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)
- Dirty schedulers: All CPU-intensive operations (rendering, encoding) use dirty CPU schedulers for true parallelization
- Public API:
CanvasCraftmodule (in-memory export, raw buffer) - DSL:
CanvasCraft.Scenemacro composes scenes and scopes AA - Encoding: WEBP (lossless); raw RGBA buffer for custom pipelines
- See
guides/PARALLELIZATION.mdfor parallel rendering examples
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 usingmixso 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 --installon macOS;build-essentialon Ubuntu)
Contributing
See CONTRIBUTING.md. CI runs format, Credo, Dialyzer, tests, and non-blocking benches.
License
MIT