Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Launch demo

Running the demo (for Wacom tablets)

  1. Install the Wacom web plugin
    (automatically included when you install any modern Wacom tablet)
  2. Open index.html in a WebKit browser
  3. Draw on canvas with a Wacom pen or check Use Mouse to use a mouse


<script type='text/javascript' src="/* YOUR PATH HERE */ploma.js"></script>
var canvas = /* YOUR <CANVAS> ELEMENT */;
var isDrawing = false;

var ploma = new Ploma(canvas);

canvas.onmousedown = function(e) {
  isDrawing = true;
  ploma.beginStroke(e.clientX, e.clientY, 1);

canvas.onmousemove = function(e) {
  if (!isDrawing) return;
  ploma.extendStroke(e.clientX, e.clientY, 1);

canvas.onmouseup = function(e) {
  isDrawing = false;
  ploma.endStroke(e.clientX, e.clientY, 1);

Full Example

Full example usage of Ploma can be found in index.html


A Ploma instance expects an HTML <canvas> Element for rendering ballpoint pen strokes given input points. Strokes are rendered using beginStroke, extendStroke, and endStroke which accept a single point's data: x-coordinate, y-coordinate and a pressure value ranging from 0-1. Pressure values can come from any input device you have access to. For Wacom tablets, pressure values can be obtained using the Wacom web plugin object element in your HTML.


Ploma(canvas) Constructor for Ploma instances. Accepts an HTML <canvas> Element element to render strokes onto.
getStrokeImageData(strokes) Returns image data for the input stroke, against a transparent canvas, clipped to the stroke's bounds. Input stroke is to be a an array of JSON objects of point data:
[{x, y, p}, {x, y, p}, ...]


clear() Clears the canvas.
beginStroke(x, y, p) Begins a new stroke containing the given point x, y and p (pressure ranging from 0-1) values.
extendStroke(x, y, p) Extends the current stroke with the given point and renders the new stroke segment to the canvas.
endStroke(x, y, p) Ends the current stroke with the given point and renders the final stroke segment to the canvas.
getStrokes() Returns an array of all strokes that have been recorded, each stroke itself is an array of point JSON objects.
[[{x, y, p}, {x, y, p}, ...],[{x, y, p}, {x, y, p}, ...],...]
setStrokes(s) Set strokes to be rendered on the canvas. Expects an array strokes, each stroke itself is an array of point JSON objects.
[[{x, y, p}, {x, y, p}, ...],[{x, y, p}, {x, y, p}, ...],...]
curStroke() Returns the current stroke of points that have been stored since the last mouse down as an array of point JSON objects.
[{x, y, p}, {x, y, p}, ...]


  • Optimize
    • asm.js?
    • Refactor texture access?
  • Rendering
    • Add inkflow anomalies
    • Finetune textures at light and heavy touches
  • Curves
    • Last mouseup stroke may be missing and not being drawn
    • Wider input filtering
    • Input downsampling?
    • Even stepping deteriorates as step size increases
  • Refactor
    • Bezier object
  • Miscellaneous
    • Try Catmull-Rom instead?
    • Switch to CIELab color space?
      • RGB of stroke is probably inaccurate, the app should probably be using black for now
  • Rewrite as stream that accepts pre-recorded strokes for non-realtime use
  • Add texturing capability
  • Refactor Point object


Ploma: High-fidelity ballpoint pen rendering for pressure sensitive tablets






No releases published


No packages published

Contributors 4