{"_id":"@documenso/skia-canvas","_rev":"2-5bc444fa56efba1be92c6ed96e9def55","name":"@documenso/skia-canvas","dist-tags":{"latest":"3.0.8-documenso.3"},"versions":{"3.0.8-documenso.0":{"name":"@documenso/skia-canvas","version":"3.0.8-documenso.0","keywords":["canvas","gpu","skia","offscreen","headless","graphic","graphics","image","images","compositing","render","vulkan","metal","pdf","svg","rust"],"author":{"name":"Christian Swinehart","email":"drafting@samizdat.co"},"license":"MIT","_id":"@documenso/skia-canvas@3.0.8-documenso.0","maintainers":[{"name":"mythie","email":"me@lucasjamessmith.me"}],"homepage":"https://skia-canvas.org","bugs":{"url":"https://github.com/documenso/skia-canvas/issues"},"dist":{"shasum":"7c7fe857f15b0661c7593c5ef95fd81eb687596c","tarball":"https://registry.npmjs.org/@documenso/skia-canvas/-/skia-canvas-3.0.8-documenso.0.tgz","fileCount":19,"integrity":"sha512-IqIkRLQbKydlfK4QcMxNdoFKuRzT62BLIBS/iPu9YWQSGnu5+Ygy0Ok6FQBwfOHclDZ5EqtZC6M/JISqJVHqfw==","signatures":[{"sig":"MEQCIFIowNVe/6PZtMeGXwq15IRMjTc6oThXnZ8difZKPunEAiBk+qHGO02xKVgrVXxotNrfJW3h7MIDRWtgTJe8Zn+FKg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":178123},"main":"./lib/index.js","types":"./lib/index.d.ts","exports":{"node":{"import":"./lib/index.mjs","require":"./lib/index.js"},"types":"./lib/index.d.ts","browser":"./lib/browser.js"},"gitHead":"91a5c002e5f92ea2945f73d97329d0ad00642699","scripts":{"test":"node --test","build":"node lib/prebuild.mjs compile","upload":"node lib/prebuild.mjs upload","install":"node lib/prebuild.mjs download --or-compile"},"_npmUser":{"name":"mythie","email":"me@lucasjamessmith.me"},"prebuild":{},"repository":{"url":"git+https://github.com/documenso/skia-canvas.git","type":"git"},"_npmVersion":"10.9.8","description":"A multi-threaded, GPU-accelerated, Canvas API for Node","directories":{},"_nodeVersion":"22.23.2","dependencies":{"detect-libc":"^2.1.1","string-split-by":"^1.0.0","follow-redirects":"^1.15.11","https-proxy-agent":"^7.0.6"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tmp":"^0.2.5","hono":"^4.9.8","nock":"^14.0.10","@types/node":"^24.5.2","@hono/node-server":"^1.19.4","cargo-cp-artifact":"^0.1"},"_npmOperationalInternal":{"tmp":"tmp/skia-canvas_3.0.8-documenso.0_1786602392201_0.8281722546106818","host":"s3://npm-registry-packages-npm-production"}},"3.0.8-documenso.3":{"name":"@documenso/skia-canvas","version":"3.0.8-documenso.3","description":"A multi-threaded, GPU-accelerated, Canvas API for Node","author":{"name":"Christian Swinehart","email":"drafting@samizdat.co"},"license":"MIT","homepage":"https://skia-canvas.org","repository":{"type":"git","url":"git+https://github.com/documenso/skia-canvas.git"},"bugs":{"url":"https://github.com/documenso/skia-canvas/issues"},"publishConfig":{"access":"public"},"main":"./lib/index.js","exports":{"node":{"import":"./lib/index.mjs","require":"./lib/index.js"},"browser":"./lib/browser.js","types":"./lib/index.d.ts"},"scripts":{"build":"node lib/prebuild.mjs compile","install":"node lib/prebuild.mjs download --or-compile","upload":"node lib/prebuild.mjs upload","test":"node --test"},"dependencies":{"detect-libc":"^2.1.1","follow-redirects":"^1.15.11","https-proxy-agent":"^7.0.6","string-split-by":"^1.0.0"},"devDependencies":{"@hono/node-server":"^1.19.4","@types/node":"^24.5.2","cargo-cp-artifact":"^0.1","hono":"^4.9.8","nock":"^14.0.10","tmp":"^0.2.5"},"keywords":["canvas","gpu","skia","offscreen","headless","graphic","graphics","image","images","compositing","render","vulkan","metal","pdf","svg","rust"],"prebuild":{"darwin-arm64.gz":"sha256:1b022388703bf42c365ca103f74eed0d8948738cb92be37909ec7f4550106425","darwin-x64.gz":"sha256:810c82082c118cb35655d6c77fca0a9a5b82e91bfde8eda1e97632fe67684f09","linux-arm64-glibc.gz":"sha256:89d25afaef3809cd128c171948eb3db199401d5f35c85f1e48ecdd3f99660aeb","linux-arm64-musl.gz":"sha256:b90f46c146770b2716b058e2cf901e663670af27aca55253940e543df233515a","linux-x64-glibc.gz":"sha256:7c499a27924ce92ff3f695a18657fc99a31538cf6edff788ed51666e3fcdb38e","linux-x64-musl.gz":"sha256:af28724e11f5ce76d6beb81ba06d6cb8d41e6b79185c82e23e66730bfa7e62fa","win32-arm64.gz":"sha256:71390d1c7aefbc2ca7136560648e1ca6f9bca779b68ff81c5dadbf71e5767193","win32-x64.gz":"sha256:d1100677d52e317a999ba75abcc7646b75a290292df66773c6c372e730ebeb28"},"_id":"@documenso/skia-canvas@3.0.8-documenso.3","gitHead":"bee09f613c66d4e7853de8e92bbad2a9feb77d0c","types":"./lib/index.d.ts","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-IeEr7RxJigCzidsRIQu6tcMX9+bt0tG3Y5kMArCPF4/jEgunPVx3qd7Wyh34mtSNufpzmVwazxR+U2BIz3iYvQ==","shasum":"0e48807d36831c3e714e21d7036e88a38c460a16","tarball":"https://registry.npmjs.org/@documenso/skia-canvas/-/skia-canvas-3.0.8-documenso.3.tgz","fileCount":19,"unpackedSize":178917,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBhkVi5GXL5UJ7gDu3msSYzEmRvRPFRNz2Bg//J6uAHaAiEAoLGnCL9KMUr5PUoIrwkC618hmcMFGUFLi8eSQ05scgk="}]},"_npmUser":{"name":"mythie","email":"me@lucasjamessmith.me"},"directories":{},"maintainers":[{"name":"mythie","email":"me@lucasjamessmith.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/skia-canvas_3.0.8-documenso.3_1786619836126_0.2897019645430261"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-13T06:26:32.026Z","modified":"2026-08-13T11:17:16.461Z","3.0.8-documenso.0":"2026-08-13T06:26:32.347Z","3.0.8-documenso.3":"2026-08-13T11:17:16.286Z"},"bugs":{"url":"https://github.com/documenso/skia-canvas/issues"},"author":{"name":"Christian Swinehart","email":"drafting@samizdat.co"},"license":"MIT","homepage":"https://skia-canvas.org","keywords":["canvas","gpu","skia","offscreen","headless","graphic","graphics","image","images","compositing","render","vulkan","metal","pdf","svg","rust"],"repository":{"type":"git","url":"git+https://github.com/documenso/skia-canvas.git"},"description":"A multi-threaded, GPU-accelerated, Canvas API for Node","maintainers":[{"name":"mythie","email":"me@lucasjamessmith.me"}],"readme":"<a href=\"https://skia-canvas.org\">\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/hero-dark@2x.png\">\n  <img alt=\"Skia Canvas\" src=\"docs/assets/hero@2x.png\">\n</picture>\n\n</a>\n\n---\n\n<div align=\"center\">\n  <a href=\"http://skia-canvas.org/getting-started\">Getting Started</a> <span>&nbsp;&nbsp;·&nbsp;&nbsp;</span>\n  <a href=\"http://skia-canvas.org/api\">Documentation</a> <span>&nbsp;&nbsp;·&nbsp;&nbsp;</span>\n  <a href=\"http://skia-canvas.org/releases\">Release Notes</a>  <span>&nbsp;&nbsp;·&nbsp;&nbsp;</span>\n  <a href=\"https://github.com/samizdatco/skia-canvas/discussions\">Discussion Forum</a>\n</div>\n\n<div align=\"center\">\n\n### [Version 3.0 now available](https://github.com/samizdatco/skia-canvas/discussions/255)\n\n</div>\n\n---\n\nSkia Canvas is a Node.js implementation of the HTML Canvas drawing [API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) for both on- and off-screen rendering. Since it uses Google’s [Skia](https://skia.org) graphics engine, its output is very similar to Chrome’s [`<canvas>`](https://html.spec.whatwg.org/multipage/canvas.html) element — though it's also capable of things the browser’s Canvas still can't achieve.\n\nIn particular, Skia Canvas:\n\n  - generates images in vector (PDF & SVG) as well as bitmap (JPEG, PNG, & WEBP) formats\n  - can draw to interactive GUI [windows][window] and provides a browser-like [event][win_bind] framework\n  - can save images to [files][toFile], encode to [dataURL][toURL] strings, and return [Buffers][toBuffer] or [Sharp][sharp] objects\n  - uses native threads in a [user-configurable][multithreading] worker pool for asynchronous rendering and file I/O\n  - can create [multiple ‘pages’][newPage] on a given canvas and then [output][toFile] them as a single, multi-page PDF or an image-sequence saved to multiple files\n  - can [simplify][p2d_simplify], [blunt][p2d_round], [combine][bool-ops], [excerpt][p2d_trim], and [atomize][p2d_points] Bézier paths using [efficient](https://www.youtube.com/watch?v=OmfliNQsk88) boolean operations or point-by-point [interpolation][p2d_interpolate]\n  - provides [3D perspective][createProjection()] transformations in addition to [scaling][scale()], [rotation][rotate()], and [translation][translate()]\n  - can fill shapes with vector-based [Textures][createTexture()] in addition to bitmap-based [Patterns][createPattern()] and supports line-drawing with custom [markers][lineDashMarker]\n  - supports the full set of [CSS filter][filter] image processing operators\n  - offers rich typographic control including:\n    - multi-line, [word-wrapped][textwrap] text\n    - line-by-line [text metrics][c2d_measuretext]\n    - small-caps, ligatures, and other opentype features accessible using standard [font-variant][fontvariant] syntax\n    - proportional [letter-spacing][letterSpacing], [word-spacing][wordSpacing], and [leading][c2d_font]\n    - support for [variable fonts][VariableFonts] and transparent mapping of weight values\n    - use of non-system fonts [loaded][fontlibrary-use] from local files\n  - can be used for server-side image rendering on standard Linux hosts and ‘serverless’ platforms like Vercel and AWS Lambda\n\n## Installation\n\nIf you’re running on a supported platform, installation should be as simple as:\n```bash\nnpm install skia-canvas\n```\n\nThis will download a pre-compiled library from the project’s most recent [release](https://github.com/samizdatco/skia-canvas/releases).\n\n\n### `pnpm`\nIf you use the `pnpm` package manager, it will not download `skia-canvas`'s platform-native binary unless you explicitly allow it. You can do this interactively via the ‘approve builds’ command (note that you need to press `<space>` to toggle the selection and then `<enter>` to proceed):\n\n```bash\npnpm install skia-canvas\npnpm approve-builds\n```\nIn non-interactive scenarios (like building via CI), you can approve the build step when you add `skia-canvas` to your project:\n\n```bash\npnpm install skia-canvas --allow-build=skia-canvas\n```\n\nAlternatively, you can add a [`pnpm.onlyBuiltDependencies`](https://pnpm.io/9.x/package_json#pnpmonlybuiltdependencies) entry to your `package.json` file to mark the build-step as allowed:\n```json\n{\n  \"pnpm\": {\n    \"onlyBuiltDependencies\": [\"skia-canvas\"]\n  }\n}\n```\n\n\n\n## Platform Support\n\nSkia Canvas runs on Linux, macOS, or Windows as well as serverless platforms like Vercel and AWS Lambda. Precompiled versions of the library’s native code will be automatically downloaded in the appropriate architecture (`arm64` or `x64`) when you install it via npm.\n\nThe underlying Rust library uses [N-API][node_napi] v8 which allows it to run on all [currently supported](https://nodejs.org/en/about/previous-releases) Node.js releases, and it is backward compatible with versions going back to v12.22+, v14.17+, v15.12+, and v16+.\n\n### Linux\n\nThe library is compatible with Linux systems using [glibc](https://www.gnu.org/software/libc/) 2.28 or later as well as Alpine Linux and the [musl](https://musl.libc.org) C library it favors. It will make use of the system’s `fontconfig` settings in `/etc/fonts` if they exist but will otherwise fall back to using a [placeholder configuration](https://github.com/samizdatco/skia-canvas/blob/main/lib/fonts/fonts.conf), looking for installed fonts at commonly used Linux paths.\n\n### Docker\n\nIf you are setting up a [Dockerfile](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) that uses [`node`](https://hub.docker.com/_/node) as its basis, the simplest approach is to set your `FROM` image to one of the (Debian-derived) defaults like `node:lts`, `node:22`, `node:24-bookworm`, or simply:\n```dockerfile\nFROM node\n```\n\nIf you wish to use Alpine as the underlying distribution, you can start with something along the lines of:\n\n```dockerfile\nFROM node:alpine\n```\n\n### AWS Lambda\n\nSkia Canvas depends on libraries that aren't present in the standard Lambda [runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). You can add these to your function by uploading a ‘[layer](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html)’ (a zip file containing the required libraries and `node_modules` directory) and configuring your function to use it.\n\n\n<details><summary>\n\n**Detailed AWS instructions**\n\n</summary>\n\n#### Adding the Skia Canvas layer to your AWS account\n\n1. Look in the **Assets** section of Skia Canvas’s [current release](https://github.com/samizdatco/skia-canvas/releases/latest) and download the `aws-lambda-x64.zip` or `aws-lambda-arm64.zip` file (depending on your architecture) but don’t decompress it\n2. Go to the AWS Lambda [Layers console](https://console.aws.amazon.com/lambda/home/#/layers) and click the **Create Layer** button, then fill in the fields:\n  - **Name**: `skia-canvas` (or whatever you want)\n  - **Description**: you might want to note the Skia Canvas version here\n  - **Compatible architectures**: select **x86_64** or **arm64** depending on which zip you chose\n  - **Compatible runtimes**: select **Node.js 22.x** (and/or 20.x)\n3. Click the **Choose file** button and select the zip file you downloaded in Step 1, then click **Create**\n\nAlternatively, you can use the [`aws` command line tool](https://github.com/aws/aws-cli) to create the layer. This bash script will fetch the skia-canvas version of your choice and make it available to your Lambda functions.\n```sh\n#!/usr/bin/env bash\nVERSION=3.0.8 # the skia-canvas version to include\nPLATFORM=arm64 # arm64 or x64\n\ncurl -sLO https://github.com/samizdatco/skia-canvas/releases/download/v${VERSION}/aws-lambda-${PLATFORM}.zip\naws lambda publish-layer-version \\\n    --layer-name \"skia-canvas\" \\\n    --description \"Skia Canvas ${VERSION} layer\" \\\n    --zip-file \"fileb://aws-lambda-${PLATFORM}.zip\" \\\n    --compatible-runtimes \"nodejs20.x\" \"nodejs22.x\" \\\n    --compatible-architectures \"${X/#x/x86_}\"\n```\n\n#### Using the layer in a Lambda function\n\nYou can now use this layer in any function you create in the [Functions console](https://console.aws.amazon.com/lambda/home/#/functions). After creating a new function, click the **Add a Layer** button and you can select your newly created Skia Canvas layer from the **Custom Layers** layer source.\n\nNote that the layer only includes Skia Canvas and its dependencies—any other npm modules you want to use will need to be bundled into your function. To prevent the `skia-canvas` module from being doubly-included, make sure you add it to the  `devDependencies` section (**not** the regular `dependencies` section) of your package.json file.\n\n</details>\n\n\n### Next.js / Webpack\n\nIf you are using a framework like Next.js that bundles your server-side code with Webpack, you'll need to mark `skia-canvas` as an ‘external’, otherwise its platform-native binary file will be excluded from the final build. Try adding these options to your `next.config.ts` file:\n\n```js\nconst nextConfig: NextConfig = {\n  serverExternalPackages: ['skia-canvas'],\n  webpack: (config, options) => {\n    if (options.isServer){\n      config.externals = [\n        ...config.externals,\n        {'skia-canvas': 'commonjs skia-canvas'},\n      ]\n    }\n    return config\n  }\n};\n```\n\n\n## Compiling from Source\n\nIf prebuilt binaries aren’t available for your system you’ll need to compile the portions of this library that directly interface with Skia.\n\nStart by installing:\n\n  1. A recent version of `git` (older versions have difficulties with Skia's submodules)\n  2. The [Rust compiler](https://www.rust-lang.org/tools/install) and cargo package manager using [`rustup`](https://rust-lang.github.io/rustup/)\n  3. A C compiler toolchain (either LLVM/Clang or MSVC)\n  4. Python 3 (used by Skia's [build process](https://skia.org/docs/user/build/))\n  5. The [Ninja](https://ninja-build.org) build system\n  6. On Linux: Fontconfig and OpenSSL\n\n[Detailed instructions](https://github.com/rust-skia/rust-skia#building) for setting up these dependencies on different operating systems can be found in the ‘Building’ section of the Rust Skia documentation. The Dockerfiles in the [containers](https://github.com/samizdatco/skia-canvas/tree/main/containers) directory may also be useful for identifying needed dependencies. 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).\n\n## Global Settings\n\n> There are a handful of settings that can only be configured at launch and will apply to all the canvases you create in your script. The sections below describe the different [environment variables][node_env] you can set to make global changes. You can either set them as part of your command line invocation, or place them in a `.env` file in your project directory and use Node 20's [`--env-file` argument][node_env_arg] to load them all at once.\n\n### Multithreading\n\nWhen rendering canvases in the background (e.g., by using the asynchronous [toFile][toFile] or [toBuffer][toBuffer] methods), tasks are spawned in a thread pool managed by the [rayon][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][canvas] object's [`engine.threads`][engine] property. If you wish to override this default, you can set the `SKIA_CANVAS_THREADS` environment variable to your preferred value.\n\nFor example, you can limit your asynchronous processing to two simultaneous tasks by running your script with:\n```bash\nSKIA_CANVAS_THREADS=2 node my-canvas-script.js\n```\n\n### Argument Validation\n\nThere are a number of situations where the browser API will react to invalid arguments by silently ignoring the method call rather than throwing an error. For example, these lines will simply have no effect:\n\n```js\nctx.fillRect(0, 0, 100, \"october\")\nctx.lineTo(NaN, 0)\n```\n\n\nSkia Canvas does its best to emulate these quirks, but allows you to opt into a stricter mode in which it will throw TypeErrors in these situations (which can be useful for debugging).\n\nSet the `SKIA_CANVAS_STRICT` environment variable to `1` or `true` to enable this mode.\n\n## Example Usage\n\n### Generating image files\n\n```js\nimport {Canvas} from 'skia-canvas'\n\nlet canvas = new Canvas(400, 400),\n    ctx = canvas.getContext(\"2d\"),\n    {width, height} = canvas;\n\nlet sweep = ctx.createConicGradient(Math.PI * 1.2, width/2, height/2)\nsweep.addColorStop(0, \"red\")\nsweep.addColorStop(0.25, \"orange\")\nsweep.addColorStop(0.5, \"yellow\")\nsweep.addColorStop(0.75, \"green\")\nsweep.addColorStop(1, \"red\")\nctx.strokeStyle = sweep\nctx.lineWidth = 100\nctx.strokeRect(100,100, 200,200)\n\n// render to multiple destinations using a background thread\nasync function render(){\n  // save a ‘retina’ image...\n  await canvas.saveAs(\"rainbox.png\", {density:2})\n  // ...or use a shorthand for canvas.toBuffer(\"png\")\n  let pngData = await canvas.png\n  // ...or embed it in a string\n  let pngEmbed = `<img src=\"${await canvas.toDataURL(\"png\")}\">`\n}\nrender()\n\n// ...or save the file synchronously from the main thread\ncanvas.saveAsSync(\"rainbox.pdf\")\n```\n\n### Multi-page sequences\n\n```js\nimport {Canvas} from 'skia-canvas'\n\nlet canvas = new Canvas(400, 400),\n    ctx = canvas.getContext(\"2d\"),\n    {width, height} = canvas\n\nfor (const color of ['orange', 'yellow', 'green', 'skyblue', 'purple']){\n  ctx = canvas.newPage()\n  ctx.fillStyle = color\n  ctx.fillRect(0,0, width, height)\n  ctx.fillStyle = 'white'\n  ctx.arc(width/2, height/2, 40, 0, 2 * Math.PI)\n  ctx.fill()\n}\n\nasync function render(){\n  // save to a multi-page PDF file\n  await canvas.saveAs(\"all-pages.pdf\")\n\n  // save to files named `page-01.png`, `page-02.png`, etc.\n  await canvas.saveAs(\"page-{2}.png\")\n}\nrender()\n```\n\n### Rendering to a window\n\n```js\nimport {Window} from 'skia-canvas'\n\nlet win = new Window(300, 300)\nwin.title = \"Canvas Window\"\nwin.on(\"draw\", e => {\n  let ctx = e.target.canvas.getContext(\"2d\")\n  ctx.lineWidth = 25 + 25 * Math.cos(e.frame / 10)\n  ctx.beginPath()\n  ctx.arc(150, 150, 50, 0, 2 * Math.PI)\n  ctx.stroke()\n\n  ctx.beginPath()\n  ctx.arc(150, 150, 10, 0, 2 * Math.PI)\n  ctx.stroke()\n  ctx.fill()\n})\n```\n\n### Integrating with [Sharp.js][sharp]\n\n```js\nimport sharp from 'sharp'\nimport {Canvas, loadImage} from 'skia-canvas'\n\nlet canvas = new Canvas(400, 400),\n    ctx = canvas.getContext(\"2d\"),\n    {width, height} = canvas,\n    [x, y] = [width/2, height/2]\n\nctx.fillStyle = 'red'\nctx.fillRect(0, 0, x, y)\nctx.fillStyle = 'orange'\nctx.fillRect(x, y, x, y)\n\n// Render the canvas to a Sharp object on a background thread then desaturate\nawait canvas.toSharp().modulate({saturation:.25}).jpeg().toFile(\"faded.jpg\")\n\n// Convert an ImageData to a Sharp object and save a grayscale version\nlet imgData = ctx.getImageData(0, 0, width, height, {matte:'white', density:2})\nawait imgData.toSharp().grayscale().png().toFile(\"black-and-white.png\")\n\n// Create an image using Sharp then draw it to the canvas as an Image object\nlet sharpImage = sharp({create:{ width:x, height:y, channels:4, background:\"skyblue\" }})\nlet canvasImage = await loadImage(sharpImage)\nctx.drawImage(canvasImage, x, 0)\nawait canvas.saveAs('mosaic.png')\n```\n\n## Benchmarks\nIn these benchmarks, Skia Canvas is tested running in two modes: serial and async. When running serially, each rendering operation is awaited before continuing to the next test iteration. When running asynchronously, all the test iterations are begun at once and are executed in parallel using the library’s multi-threading support.\n\n[See full results here…](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/index.md)\n\n### [Startup latency](https://github.com/samizdatco/canvas-benchmarks/tree/main/tests/cold-start.js)\n| Library              | Per Run   | Total Time (100 iterations)                   |\n| -------------------- | --------- | --------------------------------------------- |\n| *canvaskit-wasm*     | `  25 ms` | ` 2.47 s` ![ ](./docs/assets/benchmarks.svg#cold-start_wasm)      |\n| *canvas*             | `  88 ms` | ` 8.77 s` ![ ](./docs/assets/benchmarks.svg#cold-start_canvas)    |\n| *@napi-rs/canvas*    | `  69 ms` | ` 6.87 s` ![ ](./docs/assets/benchmarks.svg#cold-start_napi)      |\n| *skia-canvas*        | `  <1 ms` | `  33 ms` ![ ](./docs/assets/benchmarks.svg#cold-start_skia-sync) |\n\n### [Bezier curves](https://github.com/samizdatco/canvas-benchmarks/tree/main/tests/beziers.js)\n| Library                                                       | Per Run   | Total Time (20 iterations)                  |\n| ------------------------------------------------------------- | --------- | ------------------------------------------- |\n| *canvaskit-wasm* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/beziers_wasm.png)            | ` 790 ms` | `15.81 s` ![ ](./docs/assets/benchmarks.svg#beziers_wasm)       |\n| *canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/beziers_canvas.png)                  | ` 486 ms` | ` 9.72 s` ![ ](./docs/assets/benchmarks.svg#beziers_canvas)     |\n| *@napi-rs/canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/beziers_napi.png)           | ` 230 ms` | ` 4.60 s` ![ ](./docs/assets/benchmarks.svg#beziers_napi)       |\n| *skia-canvas (serial)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/beziers_skia-sync.png) | ` 137 ms` | ` 2.74 s` ![ ](./docs/assets/benchmarks.svg#beziers_skia-sync)  |\n| *skia-canvas (async)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/beziers_skia-async.png) | `  28 ms` | ` 558 ms` ![ ](./docs/assets/benchmarks.svg#beziers_skia-async) |\n\n### [SVG to PNG](https://github.com/samizdatco/canvas-benchmarks/tree/main/tests/from-svg.js)\n| Library                                                        | Per Run   | Total Time (100 iterations)                  |\n| -------------------------------------------------------------- | --------- | -------------------------------------------- |\n| canvaskit-wasm                                                 | ` ————— ` | ` ————— `   *not supported*                  |\n| *canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/from-svg_canvas.png)                  | ` 122 ms` | `12.16 s` ![ ](./docs/assets/benchmarks.svg#from-svg_canvas)     |\n| *@napi-rs/canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/from-svg_napi.png)           | `  84 ms` | ` 8.42 s` ![ ](./docs/assets/benchmarks.svg#from-svg_napi)       |\n| *skia-canvas (serial)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/from-svg_skia-sync.png) | `  58 ms` | ` 5.83 s` ![ ](./docs/assets/benchmarks.svg#from-svg_skia-sync)  |\n| *skia-canvas (async)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/from-svg_skia-async.png) | `  11 ms` | ` 1.08 s` ![ ](./docs/assets/benchmarks.svg#from-svg_skia-async) |\n\n### [Scale/rotate images](https://github.com/samizdatco/canvas-benchmarks/tree/main/tests/image-blit.js)\n| Library                                                          | Per Run   | Total Time (50 iterations)                     |\n| ---------------------------------------------------------------- | --------- | ---------------------------------------------- |\n| *canvaskit-wasm* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/image-blit_wasm.png)            | ` 274 ms` | `13.72 s` ![ ](./docs/assets/benchmarks.svg#image-blit_wasm)       |\n| *canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/image-blit_canvas.png)                  | ` 283 ms` | `14.13 s` ![ ](./docs/assets/benchmarks.svg#image-blit_canvas)     |\n| *@napi-rs/canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/image-blit_napi.png)           | ` 112 ms` | ` 5.60 s` ![ ](./docs/assets/benchmarks.svg#image-blit_napi)       |\n| *skia-canvas (serial)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/image-blit_skia-sync.png) | ` 100 ms` | ` 5.00 s` ![ ](./docs/assets/benchmarks.svg#image-blit_skia-sync)  |\n| *skia-canvas (async)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/image-blit_skia-async.png) | `  19 ms` | ` 935 ms` ![ ](./docs/assets/benchmarks.svg#image-blit_skia-async) |\n\n### [Basic text](https://github.com/samizdatco/canvas-benchmarks/tree/main/tests/text.js)\n| Library                                                    | Per Run   | Total Time (200 iterations)              |\n| ---------------------------------------------------------- | --------- | ---------------------------------------- |\n| *canvaskit-wasm* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/text_wasm.png)            | `  24 ms` | ` 4.75 s` ![ ](./docs/assets/benchmarks.svg#text_wasm)       |\n| *canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/text_canvas.png)                  | `  24 ms` | ` 4.88 s` ![ ](./docs/assets/benchmarks.svg#text_canvas)     |\n| *@napi-rs/canvas* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/text_napi.png)           | `  19 ms` | ` 3.83 s` ![ ](./docs/assets/benchmarks.svg#text_napi)       |\n| *skia-canvas (serial)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/text_skia-sync.png) | `  21 ms` | ` 4.26 s` ![ ](./docs/assets/benchmarks.svg#text_skia-sync)  |\n| *skia-canvas (async)* [👁️](https://github.com/samizdatco/canvas-benchmarks/blob/main/results/darwin-arm64/2025-09-26/snapshots/text_skia-async.png) | `   4 ms` | ` 819 ms` ![ ](./docs/assets/benchmarks.svg#text_skia-async) |\n\n## Acknowledgements\n\nThis project is deeply indebted to the work of the [Rust Skia project](https://github.com/rust-skia/rust-skia) 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](https://github.com/Automattic/node-canvas) for their terrific set of unit tests. In the absence of an [Acid Test](https://www.acidtests.org) for canvas, these routines were invaluable.\n\n\n### Notable contributors\n\n- [@mpaparno](https://github.com/mpaparno) contributed support for SVG rendering, raw image-buffer handling, WEBP import/export and numerous bug fixes\n- [@Salmondx](https://github.com/Salmondx) developed the initial Raw image loading & rendering routines\n- [@lucasmerlin](https://github.com/lucasmerlin) helped get GPU rendering working on Vulkan\n- [@cprecioso](https://github.com/cprecioso) & [@saantonandre](https://github.com/saantonandre) corrected and expanded upon the TypeScript type definitions\n- [@meihuanyu](https://github.com/meihuanyu) contributed filter & path rendering fixes\n\n## Copyright\n© 2020–2025 [Samizdat Drafting Co.](https://samizdat.co)\n\n[bool-ops]: https://skia-canvas.org/api/path2d#complement-difference-intersect-union-and-xor\n[c2d_font]: https://skia-canvas.org/api/context#font\n[c2d_measuretext]: https://skia-canvas.org/api/context#measuretext\n[canvas]: https://skia-canvas.org/api/canvas\n[createProjection()]: https://skia-canvas.org/api/context#createprojection\n[createTexture()]: https://skia-canvas.org/api/context#createtexture\n[engine]: https://skia-canvas.org/api/canvas#engine\n[fontlibrary-use]: https://skia-canvas.org/api/font-library#use\n[fontvariant]: https://skia-canvas.org/api/context#fontvariant\n[lineDashMarker]: https://skia-canvas.org/api/context#linedashmarker\n[newPage]: https://skia-canvas.org/api/canvas#newpage\n[p2d_interpolate]: https://skia-canvas.org/api/path2d#interpolate\n[p2d_points]: https://skia-canvas.org/api/path2d#points\n[p2d_round]: https://skia-canvas.org/api/path2d#round\n[p2d_simplify]: https://skia-canvas.org/api/path2d#simplify\n[p2d_trim]: https://skia-canvas.org/api/path2d#trim\n[toFile]: https://skia-canvas.org/api/canvas#tofile\n[textwrap]: https://skia-canvas.org/api/context#textwrap\n[toBuffer]: https://skia-canvas.org/api/canvas#tobuffer\n[toURL]: https://skia-canvas.org/api/canvas#tourl\n[win_bind]: https://skia-canvas.org/api/window#on--off--once\n[window]: https://skia-canvas.org/api/window\n[multithreading]: https://skia-canvas.org/getting-started#multithreading\n[node_napi]: https://nodejs.org/api/n-api.html#node-api-version-matrix\n[node_env]: https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs\n[node_env_arg]: https://nodejs.org/dist/latest-v22.x/docs/api/cli.html#--env-fileconfig\n[rayon]: https://crates.io/crates/rayon\n[sharp]: https://sharp.pixelplumbing.com\n[VariableFonts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide\n[filter]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter\n[letterSpacing]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/letterSpacing\n[wordSpacing]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/wordSpacing\n[createPattern()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createPattern\n[rotate()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rotate\n[scale()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/scale\n[translate()]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate\n","readmeFilename":"README.md"}