/ploma

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

Primary LanguageJavaScript

DEMO BLOG
Launch demo plomaproject.tumblr.com

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

Usage

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

var ploma = new Ploma(canvas);
ploma.clear();

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

API

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.

Class

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}, ...]

Instance

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}, ...]

TODO

  • 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