Skia Canvas: a Node.js canvas that writes PDF and SVG, not just PNG
A multi-threaded, GPU-powered, 2D vector graphics environment for Node.js
At a glance
- What is it?
- Skia Canvas implements the HTML Canvas API for Node.js on top of Google's Skia engine, adding vector output, multi-page documents and a native window mode. It is a good fit for server-side image generation, and the wrong tool if you need a browser DOM.
- Who is it for?
- Adopt Skia Canvas if you generate images, PDFs or SVG on a server and want to reuse browser canvas code, and if you can accept a native binary plus the 4.0.0 release candidates as the current line. Do not adopt it if you need a DOM, or if you are targeting React Native or the browser, since the package resolves to a browser stub there.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap Skia Canvas fills for Node.js rendering
Browsers ship a canvas implementation. Node.js does not. If you want to draw a chart, a watermark or a composited image on a server, you either install a native binding to a 2D library or you run a headless browser and pay for the whole rendering stack. Skia Canvas takes the first route: it exposes the HTML Canvas drawing API in Node.js, backed by Google's Skia engine, and the README describes it as usable in three ways: as a spec-compliant offscreen canvas, as a windowing toolkit, and as a JavaScript interface to Skia.
The audience is narrow and identifiable. Server-side image generation is the main one, and the README names standard Linux hosts plus serverless platforms including Vercel, Cloudflare Containers and AWS Lambda. The second audience is anyone who already has canvas drawing code and wants to run it somewhere other than a browser without rewriting the drawing calls. The third is people who need output a browser canvas cannot produce: the README lists PDF and SVG alongside JPEG, PNG, WEBP and RAW, and multi-page documents written to a single PDF or an image sequence.
How the Node.js bindings and the Skia backend fit together
The repository is split cleanly. JavaScript lives in lib/, the native layer in src/, and Cargo.toml declares a cdylib crate named skia-canvas that links skia-safe 0.99.0 with the textlayout, webp and svg features. The Node binding is built with neon. That layout explains the package.json exports map: Node gets lib/index.mjs or lib/index.js, the browser condition gets lib/browser.js, and types come from lib/index.d.ts.
Rendering is described as multi-threaded. The README says the project uses a user-configurable worker pool for asynchronous rendering and file I/O, and the Cargo dependencies include rayon, crossbeam and dashmap, which is consistent with that. The example code shows the split in practice: canvas.toFile returns a promise and the README notes it renders on a background thread, while canvas.toFileSync runs on the main thread. GPU acceleration is optional, and the Cargo features list metal and vulkan as separate flags alongside a window feature that pulls in winit. So the default build is CPU rendering, and the GPU and window modes are things you opt into at compile time.
Installing skia-canvas and producing a first file
The README gives one install line for supported platforms:
npm install skia-canvasThat is the whole install story for a prebuilt binary. package.json sets engines.node to >=18, so check that first. The README points to the Getting Started page for detailed installation and runtime configuration, and the Makefile shows the fallback path when no prebuilt binary matches your machine: npm run build, which compiles lib/skia.node from Cargo.toml and src/*.rs. The package also exposes npm run download, which the Makefile describes as downloading with an --or-compile fallback.
A first real use is writing an image to disk. The README's own example constructs a Canvas, gets a 2d context, draws into it, then awaits canvas.toFile:
import {Canvas} from 'skia-canvas'
let canvas = new Canvas(400, 400),
ctx = canvas.getContext("2d")
ctx.fillStyle = 'skyblue'
ctx.fillRect(0, 0, 400, 400)
await canvas.toFile("out.png", {density: 2})After this runs you should have out.png on disk at double density. The same object also exposes canvas.png as a shorthand for canvas.toBuffer("png"), and canvas.toURL("png") for a data URL, both shown in the README example. If you want a vector result instead of a bitmap, change the filename: the README's example saves rainbox.pdf through canvas.toFileSync, and the format follows the extension.
Multi-page PDFs and reading them back as editable canvases
The extension that distinguishes this project from a plain canvas polyfill is pagination. canvas.newPage() returns a fresh context, and the README's example loops over five colors, adding pages 2 through 6. Output then goes two ways: canvas.toFile("page-{2}.png") writes a numbered image sequence, and canvas.toFile("all-pages.pdf") writes one multi-page PDF. The brace syntax in the filename is the mechanism that turns a single call into several files.
The more unusual part is the round trip. loadCanvas opens a multi-page PDF as a canvas whose pages property is iterable, and the README example sets pg.font, pg.textAlign and pg.textBaseline on each page and calls pg.fillText to stamp a page number, then writes all-pages-labeled.pdf. That is editing an existing PDF through the canvas API rather than generating a new one. The README also lists loading PDFs and SVGs as scalable vector images via loadImage. Treat this as the headline capability: most Node drawing libraries can emit a PDF, and far fewer will read one back as a drawable surface.
Where Skia Canvas is the wrong choice
The exports map is the clearest boundary. Under the browser condition, the package resolves to lib/browser.js, so code written against this package is not a portable drawing layer for the browser; the browser already has its own canvas. If your goal is one drawing module that runs in both places, you are testing the wrong assumption, and the related searches for React Native point at a different library entirely, since nothing in this repository mentions React Native.
The second boundary is the build. A prebuilt native binary means platform coverage is a distribution problem, not a code problem. If your target has no matching binary, the fallback is compiling Rust against skia-safe, which is a large dependency to build in CI. The README does not document rollback or a pure-JavaScript fallback mode, so plan for the binary to be present or plan to compile it.
The third boundary is the version line. The most recent releases are v4.0.0-rc2 and v4.0.0-rc1, both dated 2026-09-24 and 2026-09-23, with v3.0.8 from 2025-09-25 as the last stable tag. A release candidate in package.json means the 4.x API is still settling. If you need a frozen API, the stable line is 3.0.8, and you should confirm whether the features you want exist there before building on 4.0.0-rc2.
Skia Canvas compared with node-canvas
The obvious comparison is node-canvas, and the difference is in the output formats and the pagination model. node-canvas targets the same browser canvas API for Node.js, but it is a bitmap-oriented library: you get a canvas, you draw, you encode to PNG, JPEG or PDF. Skia Canvas inverts part of that. It writes PDF and SVG through the same toFile call by extension, and the README's multi-page example shows one canvas producing a PDF whose pages can later be reopened with loadCanvas and drawn on.
There is a second difference in the rendering path. The Cargo features expose metal and vulkan as build flags, and the README describes GPU acceleration as optional, so a build can be compiled with GPU backends. The README also describes the worker pool as user-configurable for asynchronous rendering and file I/O. If your workload is a queue of image jobs, that threading model is the part to evaluate, and it is not something a plain bitmap canvas library exposes in the same way. If your workload is one small PNG per request, the extra surface buys you nothing and a smaller library will do.
Licence and the cost of staying current
package.json and Cargo.toml both declare MIT, and the repository carries a LICENSE file at the top level. That is a permissive licence, and it is worth noting that the package links skia-safe and therefore the Skia engine; if you redistribute binaries, check the licences of the bundled native components rather than assuming the MIT declaration covers everything in the artifact. This is not legal advice, and the repository's own LICENSE file is the thing to read.
Upgrade cost is dominated by the native binary, not by the JavaScript. The Makefile's release target refuses to run unless package.json is committed on main and there are no unpushed commits, and it derives the tag from the package version, which tells you the published artifact and the source tag are meant to move together. The practical consequence for a consumer is that a version bump can mean a new binary download, and a platform that had a binary for one release may not have one for the next. The repository also carries a vendor/ directory and a containers/ directory, and the Makefile has a containers target keyed to a monthly version, which suggests container images are part of the distribution story. The README does not document a support window or an LTS policy, so pinning a version and testing the upgrade yourself is the only way to know.
Editorial conclusion
Adopt Skia Canvas if you generate images, PDFs or SVG on a server and want to reuse browser canvas code, and if you can accept a native binary plus the 4.0.0 release candidates as the current line. Do not adopt it if you need a DOM, or if you are targeting React Native or the browser, since the package resolves to a browser stub there. Before committing, check that your platform has a prebuilt binary for 4.0.0-rc2, or budget for a Rust toolchain, because the Makefile's build target compiles lib/skia.node from src/.
Frequently asked questions
What is Skia used for in Skia Canvas?
Skia is the rendering engine underneath the package. The README describes Skia Canvas as a JavaScript interface for the Skia graphics library, using web APIs as a front end to Google's imaging engine, with rendering done in native code and optional GPU acceleration.
What companies use Skia?
The README does not name any companies using Skia. It only states that the project renders with Google's Skia imaging engine and points to skia.org for the engine itself.
What is the purpose of using canvas in Skia Canvas?
The canvas API is the drawing surface the package exposes. The README says it implements the HTML Canvas drawing API in Node.js so the same drawing code you would write for a browser can run on servers and in other headless contexts to generate image files and buffers.
Which is better, canvas or SVG?
Skia Canvas does not force the choice: the README lists both vector formats (PDF and SVG) and bitmap formats (JPEG, PNG, WEBP and RAW) as outputs, with the format following the file extension passed to toFile. Which one suits you depends on whether you need scalable vector output or a raster image.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/samizdatco-skia-canvas)