
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
skia-canvas
Advanced tools
Skia Canvas is a browser-less implementation of the HTML Canvas drawing API for Node.js. It is based on Google’s Skia graphics engine and, accordingly, produces very similar results to Chrome’s <canvas>
element. The library is well suited for use on desktop machines where you can render hardware-accelerated graphics to a window and on the server where it can output a variety of image formats.
While the primary goal of this project is to provide a reliable emulation of the standard API according to the spec, it also extends it in a number of areas to take greater advantage of Skia's advanced graphical features and provide a more expressive coding environment.
In particular, Skia Canvas:
If you’re running on a supported platform, installation should be as simple as:
npm install skia-canvas
This will download a pre-compiled library from the project’s most recent release.
The underlying Rust library uses N-API v8 which allows it to run on Node.js versions:
Pre-compiled binaries are available for:
Nearly everything you need is statically linked into the library. A notable exception is the Fontconfig library which must be installed separately if you’re running on Linux.
The library is compatible with Linux systems using glibc 2.28 or later as well as Alpine Linux (x64 & arm64) and the musl C library it favors. In both cases, Fontconfig must be installed on the system for skia-canvas
to operate correctly.
If you are setting up a Dockerfile that uses node
as its basis, the simplest approach is to set your FROM
image to one of the (Debian-derived) defaults like node:lts
, node:18
, node:16
, node:14-buster
, node:12-buster
, node:bullseye
, node:buster
, or simply:
FROM node
You can also use the ‘slim’ image if you manually install fontconfig:
FROM node:slim
RUN apt-get update && apt-get install -y -q --no-install-recommends libfontconfig1
If you wish to use Alpine as the underlying distribution, you can start with something along the lines of:
FROM node:alpine
RUN apk update && apk add fontconfig
If prebuilt binaries aren’t available for your system you’ll need to compile the portions of this library that directly interface with Skia.
Start by installing:
rustup
Detailed instructions for setting up these dependencies on different operating systems can be found in the ‘Building’ section of the Rust Skia documentation. Once all the necessary compilers and libraries are present, running npm run build
will give you a usable library (after a fairly lengthy compilation process).
When rendering canvases in the background (e.g., by using the asynchronous saveAs or toBuffer methods), tasks are spawned in a thread pool managed by the rayon library. By default it will create up to as many threads as your CPU has cores. You can see this default value by inspecting any Canvas object's engine.threads
property. If you wish to override this default, you can set the SKIA_CANVAS_THREADS
environment variable to your preferred value.
For example, you can limit your asynchronous processing to two simultaneous tasks by running your script with:
SKIA_CANVAS_THREADS=2 node my-canvas-script.js
import {Canvas} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d"),
{width, height} = canvas;
let sweep = ctx.createConicGradient(Math.PI * 1.2, width/2, height/2)
sweep.addColorStop(0, "red")
sweep.addColorStop(0.25, "orange")
sweep.addColorStop(0.5, "yellow")
sweep.addColorStop(0.75, "green")
sweep.addColorStop(1, "red")
ctx.strokeStyle = sweep
ctx.lineWidth = 100
ctx.strokeRect(100,100, 200,200)
// render to multiple destinations using a background thread
async function render(){
// save a ‘retina’ image...
await canvas.saveAs("rainbox.png", {density:2})
// ...or use a shorthand for canvas.toBuffer("png")
let pngData = await canvas.png
// ...or embed it in a string
let pngEmbed = `<img src="${await canvas.toDataURL("png")}">`
}
render()
// ...or save the file synchronously from the main thread
canvas.saveAsSync("rainbox.pdf")
import {Canvas} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d"),
{width, height} = canvas
for (const color of ['orange', 'yellow', 'green', 'skyblue', 'purple']){
ctx = canvas.newPage()
ctx.fillStyle = color
ctx.fillRect(0,0, width, height)
ctx.fillStyle = 'white'
ctx.arc(width/2, height/2, 40, 0, 2 * Math.PI)
ctx.fill()
}
async function render(){
// save to a multi-page PDF file
await canvas.saveAs("all-pages.pdf")
// save to files named `page-01.png`, `page-02.png`, etc.
await canvas.saveAs("page-{2}.png")
}
render()
import {Window} from 'skia-canvas'
let win = new Window(300, 300)
win.title = "Canvas Window"
win.on("draw", e => {
let ctx = e.target.canvas.getContext("2d")
ctx.lineWidth = 25 + 25 * Math.cos(e.frame / 10)
ctx.beginPath()
ctx.arc(150, 150, 50, 0, 2 * Math.PI)
ctx.stroke()
ctx.beginPath()
ctx.arc(150, 150, 10, 0, 2 * Math.PI)
ctx.stroke()
ctx.fill()
})
This project is deeply indebted to the work of the Rust Skia project whose Skia bindings provide a safe and idiomatic interface to the mess of C++ that lies underneath. Many thanks to the developers of node-canvas for their terrific set of unit tests. In the absence of an Acid Test for canvas, these routines were invaluable.
© 2020–2025 Samizdat Drafting Co.
🥚 ⟩ [v2.0.2] ⟩ Jan 28, 2025
fontHinting
attribute (off by default to better match font weights in browser rendering). Setting it to true
may result in crisper edges but adds some weight to the font.letterSpacing
no longer indents text at beginning of lineletterSpacing
now properly handles negative valuesmeasureText()
][measureText()]
ctx.font
string (NB: this is likely to cause vertical shifts for non-alphabetic
baselines)middle
& hanging
to better match browsersactualBoundingBox*
& lines[].x/y/width/height
rectangles returned by measureText() are now just the glyph-occupied area, not the whole line-height of the textblockactualBoundingBoxLeft
(positive values now mean left of the origin)lines[].baseline
now corresponds to the selected ctx.textBaseline
, previously it was always the alphabetic baselineFAQs
A GPU-accelerated Canvas Graphics API for Node
The npm package skia-canvas receives a total of 11,956 weekly downloads. As such, skia-canvas popularity was classified as popular.
We found that skia-canvas demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.