
Atrament is a JavaScript library that enables smooth, natural drawing and handwriting on HTML canvas elements. Designed to mimic how physical ink interacts with paper.
Features:
- Modes: Supports Draw, Fill, and Erase modes for versatile interaction.
- Smoothing: Features adjustable adaptive smoothing to refine stroke appearance.
- Event Tracking: Emits events for stroke tracking. This allows for “replay” or reconstruction of drawings.
- Customization: Adjustable line thickness and color settings.
- Input Support: Handles mouse, touch, and pressure-sensitive tablet input events.
- Fill Mode: Includes a web worker-based flood fill algorithm. This prevents UI blocking during large fill operations.
Use Cases:
- Digital Signature Pads: Perfect for capturing smooth, natural-looking signatures in forms.
- Educational Tools: Useful for interactive whiteboards where teachers or students can write or solve problems freehand.
- Annotation Tools: Enables you to highlight or annotate images and documents directly within the browser.
- Creative Drawing Apps: Serves as the core engine for simple sketching applications that require responsive performance.
How To Use It:
1. Install and import atrament with NPM.
# NPM $ npm install atrament
import Atrament from 'atrament';
// From version 5.0.0, the fill functionality must be imported separately: import fill from 'atrament/fill';
2. Create a canvas element in your HTML:
<!-- Define the canvas element with specific dimensions --> <canvas id="sketchpad" width="600" height="450"></canvas>
3. Initialize Atrament in your JavaScript file.
// Select the canvas element from the DOM
const canvas = document.querySelector('#sketchpad');
// Initialize Atrament with the canvas element
const sketchpad = new Atrament(canvas, {
width: 600,
height: 450,
});
// Now you can start drawing on the canvas!4. All configuration options.
width(number): The width of the canvas in pixels.height(number): The height of the canvas in pixels.color(string): The drawing color (e.g., ‘#000000’, ‘rgba(255, 0, 0, 0.5)’).weight(number): The thickness of the line in pixels.mode: Drawing mode, accepts MODE_DRAW, MODE_ERASE, MODE_FILL, or MODE_DISABLED (constant).smoothing(number): The smoothing factor for strokes. Higher values mean smoother lines. Default is0.85.adaptiveStroke(boolean): Iftrue, line width changes based on speed to simulate real ink. Default istrue.pressureLow(number): Lower bound of pressure scale (0-1). Affects stroke width at low pressure.pressureHigh(number): Upper bound of pressure scale. Affects stroke width at high pressure.pressureSmoothing(number): Amount of low-pass filtering for pressure values. Default is0.3.secondaryMouseButton(boolean): Iftrue, allows drawing with the right mouse button. Default isfalse.ignoreModifiers(boolean): Iftrue, ignores strokes when keys like Alt/Ctrl are pressed. Default isfalse.recordStrokes(boolean): Iftrue, enables stroke recording events. Default isfalse.fill(Worker): Optional. Pass the importedfillmodule here to enable Fill mode.dirty: Read-only property indicating if canvas has been drawn on (boolean).
5. API methods.
clear(): Clears the canvas and resets the dirty state.beginStroke(x, y): Moves the virtual pen to the specified coordinates and starts a new stroke path.draw(x, y, previousX, previousY, pressure): Draws a stroke segment. It calculates smoothing and renders the curve.endStroke(x, y): Ends the current stroke path.destroy(): Cleans up event listeners and internal state.addEventListener(eventName, handler): Registers a callback function for a specific event.removeEventListener(eventName, handler): Removes a previously registered callback function.
6. Event handlers.
dirty: Fired when the canvas is first drawn on.clean: Fired when the canvas is cleared.strokestart: Fired when a stroke begins. Contains{ x, y }.strokeend: Fired when a stroke ends. Contains{ x, y }.fillstart: Fired when a fill operation begins. Contains{ x, y }of the click.fillend: Fired when a fill operation completes.pointerdown: Fired on the raw pointer down event, before drawing logic.pointerup: Fired on the raw pointer up event.strokerecorded: Fired when a full stroke is finished recording. RequiresrecordStrokes: true.segmentdrawn: Fired every time a segment is drawn during recording. RequiresrecordStrokes: true.
// Listen for canvas state changes:
sketchpad.addEventListener('dirty', () => {
console.info('Canvas has content:', sketchpad.dirty);
});
sketchpad.addEventListener('clean', () => {
console.info('Canvas was cleared:', sketchpad.dirty);
});FAQs:
Q: How do I implement undo and redo functionality?
A: Enable recordStrokes to capture stroke data in the strokerecorded event. Store each stroke in an array. For undo, remove the last stroke from your array, call clear() on the canvas, then loop through the remaining strokes and redraw them programmatically using beginStroke(), draw(), and endStroke(). For redo, maintain a separate array of undone strokes and replay them when needed.
Q: Can I export the canvas drawing as an image or SVG?
A: The canvas content can be exported as a raster image using the standard toDataURL() or toBlob() methods on the canvas element itself. This produces PNG or JPEG output.
Q: Why do my strokes look jagged on high-DPI displays?
A: The canvas resolution needs to match the device pixel ratio. Pass explicit width and height values to the Atrament constructor that account for window.devicePixelRatio. Multiply your desired logical dimensions by this ratio. For example, on a 2x display, a 500px logical canvas needs a 1000px actual width. Then scale the canvas element back down using CSS to maintain the correct visual size.
Q: How do I prevent drawing when users are scrolling or using gestures?
A: Set ignoreModifiers to true to block drawing when modifier keys are pressed. For touch devices, you need to handle this at the pointer event level before Atrament processes the input. Listen to the pointerdown event and call preventDefault() on multi-touch gestures. You can also temporarily set the mode to MODE_DISABLED to prevent any drawing while allowing events to fire.







