pwa-inking: Enable 2D inking for the web!

Killian McCoy
PWABuilder
Published in
4 min readJul 15, 2020

There are many ways to enable inking on the web, but they all involve their challenges to set up. Do you need to support 2D drawing or 3D rendering? How will you minimize rendering latency? How should the drawing respond to browser events like window resizing, tab switching, or zooming? What kinds of input do you want to handle? What do you want to do with a finished drawing?

The PWABuilder team built a solution that enables a basic inking experience for the modern web and addresses the above considerations. We are excited to announce the 1.0 version of pwa-inking is now available! 🎉

pwa-inking is a web component that:

  • Uses a desynchronized 2D HTML canvas.
  • Optimizes canvas redraws through the requestAnimationFrame() and requestIdleCallback() functions.
  • Resizes and refocuses with the browser.
  • Supports pointer (mouse, touch, and pen) events.
  • Offers 4 inking experiences: pen, pencil, highlighter, and eraser.
  • Allows you to copy your drawing to the clipboard and save it as a png through the native device file system.

In other words, pwa-inking can let users draw a picture as quickly as they can doodle it with a finger and then save it to their phone’s camera roll! And developer’s can make it happen by adding only a few lines of code to their existing web apps 😎

Why would I use this? 🤔

  • It’s modern using the latest web APIs to keep your web apps fast while handling and rendering user input: touch, pen, and mouse friendly!
  • It’s ready to go wherever you want to use it in your web apps, right on your homepage or nested in another web component.
  • It’s customizable through CSS shadow parts and you can set any configuration of the included tools you want to use.
  • It’s free to use and open-source because why not!

Getting Started

Install it 💻

You can install this component through npm or a script tag. If you are already using npm, we recommend installing the pwa-inking npm package. Otherwise, the script tag works fine for simple use cases.

Script tag

  1. Add this script tag in the head of your index.html

NPM

  1. Run this command in your project directory:
npm install @pwabuilder/pwa-inking

2. Add this import statement to your script file:

import @pwabuilder/pwa-inking

Pick your starter 🌊🌿🔥

This component can be used with or without an included toolbar:

Canvas with toolbar 🛠

A screenshot of the pwa-inking component with the default toolbar.
A screenshot of the pwa-inking component with the default toolbar.

The default and recommended experience is to add an inking-component with a nested inking-toolbar to get the most functionality for the least amount of code:

Try it: live | code

Canvas without toolbar 🚫🛠

A screenshot of the pwa-inking component without a toolbar.
A screenshot of the pwa-inking component without a toolbar.

Some advanced users might want to implement their own toolbar or control the canvas purely through JavaScript, so the inking-canvas can also be used alone and controlled via its APIs:

Try it: live | code

Customize it 💅

Pick your tools 🧰

A screenshot of pwa-inking with a custom toolbar positioned in the bottom right corner.
A screenshot of pwa-inking with a custom toolbar positioned in the bottom right corner.

Only want some of the tools? You can specify the toolbar’s contents and even change its position and orientation on the canvas:

Try it: live | code

Modify the CSS ✨

A screenshot of pwa-inking with customized styles on the toolbar and the canvas.
A screenshot of pwa-inking with customized styles on the toolbar and the canvas.

The canvas and the toolbar tool styles are completely customizable through CSS shadow parts:

Try it: live | code

Limitations ⚠

At the time of this release, browsers do not fully support features that impact the inking experience.

Pointer event properties 👇

Some pointer event properties that could be used to influence stroke behavior are available today but do not yet report meaningful data. These include:

  • tangentialPressure
  • tiltX
  • tiltY
  • twist

The pointer event properties utilized in this release to influence stroke size are width and pressure.

Low latency canvas 🎨

The 2D canvas contexts of this web component rely on the availability of the desynchronized attribute to support low latency.

If a browser does not recognize this attribute, then the canvas contexts are established without it.

⚠ A Chromium bug related to image extraction of the low latency canvas is preventing this web component’s copy & save features from working on impacted platforms. The fix has been applied the Chromium source code, but many Chromium-based browsers (including the stable versions of Edge and Chrome) have not received it yet. We have a GitHub issue tracking this.

requestIdleCallback() 💤

The ability to process inking component events in a way that yields to other activity on the main thread is made possible through the requestIdleCallback() function, but it is not widely supported across browsers yet.

If a browser does not support this API, the impacted inking component calls are run instead as IIFE’s (immediately invoked function expressions).

Clipboard API 📋

The canvas state is copied as a png image to the browser clipboard through the Clipboard APIs.

If a browser does not support navigator.clipboard or ClipboardItem, then a failure toast (and console error message if you clone and run the source code locally) will appear when a user clicks the copy button.

What’s next ❔

We are still planning our roadmap and we would love to hear from you! Let the PWABuilder team know what you think of this version and what you think should come next.

Find us on Twitter (@pwabuilder) and engage with us on GitHub.

Thanks for reading! 😊

--

--