Skip to content
D
Documentation

Draw on a whiteboard

how-to
3 min readUpdated

Use a mounted canvas to draw freehand marks, change a committed mark's style, and remove a mark. Draw a closed outline when you want a freehand shape: it remains ink, not a diagram node.

When to use this

Use the freehand tool for annotations, sketches, arrows, and outlines that belong on top of a diagram. A committed mark is a vector stroke, not a screenshot. The live draw preview follows the pointer; releasing commits the stroke to the mounted diagram.

DiagramInstance is the mounted canvas facade: its getModel() reaches the diagram data and its renderNow() repaints after a change. createDrawTool creates a tool for that live canvas, and registerTool connects it to the canvas's pointer-tool registry. The instance satisfies WhiteboardHost, which the tool factory accepts.

Mount the canvas and draw

  1. Start with an empty diagram and a red pen; draw a line or return to its starting point to make a closed outline.
  2. Register the draw tool after the canvas instance exists. Use the toolbar buttons to recolor the first committed mark or remove it.

In the JavaScript, Angular, React, and Vue examples, Edit first mark changes the live stroke's style and Erase first mark removes it from the live model. Either button does nothing until you draw. Those examples hold the mounted instance in the framework's normal lifecycle state and unregister the tool when that owner unmounts.

js
import { render } from '@grafloria/element';
import { createDrawTool, registerTool } from '@grafloria/renderer';

const app = document.getElementById('app');
if (!app) throw new Error('Missing #app');

const toolbar = document.createElement('div');
const canvas = document.createElement('div');
canvas.style.height = '400px';
app.append(toolbar, canvas);

const editButton = document.createElement('button');
editButton.textContent = 'Edit first mark';
const eraseButton = document.createElement('button');
eraseButton.textContent = 'Erase first mark';
toolbar.append(editButton, eraseButton);

const instance = render({ nodes: [], edges: [] }, canvas);
const unregisterTool = registerTool(
  createDrawTool(instance, { color: '#e11d48', width: 3, simplifyEpsilon: 0.8 })
);

editButton.addEventListener('click', () => {
  const stroke = instance.getModel().getStrokes()[0];
  if (!stroke) return;
  stroke.setStyle({ color: '#2563eb', width: 5 });
  instance.renderNow();
});

eraseButton.addEventListener('click', () => {
  const stroke = instance.getModel().getStrokes()[0];
  if (!stroke) return;
  instance.getModel().removeStroke(stroke.id);
  instance.renderNow();
});

window.addEventListener('pagehide', () => {
  unregisterTool();
  instance.dispose();
}, { once: true });

For Qwik, mount GrafloriaFlow and register the draw tool from onInit$, which receives the mounted instance. See the Qwik demo gallery for runnable Qwik examples.

The drawing interaction is the same in every binding: draw with a mouse, pen, or finger, then release to commit a stroke. For a freehand shape, trace a closed path yourself. In the JavaScript, Angular, React, and Vue examples, the first toolbar action recolors and widens the first live stroke; the second removes that whole stroke. Both update the mounted canvas, not a detached model.

Tune the pen

DrawToolOptions controls the created pen. The defaults below come from the tool implementation; simplifyEpsilon is left to the model's tuned default when omitted.

OptionTypeDefaultWhat it does
colorstring'#1f2933'Sets the ink and live preview color.
widthnumber3Sets the stroke width and preview width.
opacitynumberNot setSets ink opacity; use it for translucent highlighter marks.
simplifyEpsilonnumberModel's tuned defaultSets the Douglas–Peucker tolerance applied when the gesture commits.
labelstringNot setGives committed ink an accessible name.
activebooleantrueSet to false to create the draw tool inactive.

What to know

  • registerTool() is a registry operation, so keep its returned disposer and call it when the owning view unmounts. Registering another tool with the same id replaces the current one; disposing restores the previous registration.
  • These toolbar actions call model methods directly. They update the document and repaint, but they do not create a user-gesture history command. For command-based undo behavior, see Command history.
  • The pen stays active for pointer gestures. Do not leave multiple point-agnostic drawing modes active together.

See the pointer-driven tools

The freehand demo shows the pen on a blank canvas; drag a line and release to commit crisp vector ink.

Live demo: Freehand draw · source

The rectangle demo starts ready for a drag that creates a box node, rather than a stroke.

Live demo: Rectangle tool · source

The eraser demo loads with three parallel strokes; its pointer sweep removes whole strokes.

Live demo: Eraser · source

The stroke-edit demo loads with the draw and edit controls; draw ink, choose edit, and drag a committed mark to translate it.

Live demo: Stroke edit · source

Was this page helpful?

Draw on a whiteboard — Grafloria · GPT-6 Luna