Fabric.js Signature Pad — Free-Drawing Canvas Export Snippet

Fabric.js Signature Pad · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

What's included

Features

Free-drawing mode
isDrawingMode: true turns pointer input into smoothed Path strokes automatically.
Configurable PencilBrush
Width and color are live properties read fresh on every new stroke.
Multi-color signing
Swatch buttons swap freeDrawingBrush.color without affecting past strokes.
High-resolution export
toDataURL multiplier: 2 rasterizes at double the on-screen resolution.
Clean clear behavior
clear() plus an explicit backgroundColor reset keeps the paper tone.
Guide baseline
A dashed CSS line hints where to sign without being part of the canvas.
Touch-ready by default
Fabric\u2019s pointer handling works with mouse, pen, and touch input alike.
Real exportable image
The export button produces an actual PNG data URL, previewed inline.

About this UI Snippet

Fabric.js Signature Pad — Free Drawing and Canvas Export Explained

Screenshot of the Fabric.js Signature Pad snippet rendered live

Capturing a signature looks simple — draw on a canvas, save the pixels — but doing it well means smooth freehand strokes, adjustable pen properties, and a reliable way to get the result back out as an image. Fabric.js's drawing mode and toDataURL() handle all three without any manual pointer-event math.

Turning a Fabric canvas into a drawing surface

Setting isDrawingMode: true on a fabric.Canvas switches it from an object-selection surface to a freehand drawing surface: pointer-down starts a stroke, pointer-move extends it, and pointer-up commits it as a fabric.Path object. Fabric handles the pointer-to-path conversion internally, smoothing the raw input points into a single SVG-style path rather than leaving you with a polyline of straight segments.

Configuring the brush

canvas.freeDrawingBrush holds the active brush instance. This snippet explicitly assigns new fabric.PencilBrush(canvas) — Fabric's default freehand brush, which produces a single continuous stroke with a flat width — rather than relying on whatever the canvas defaulted to, so that width and color are guaranteed configurable properties on a known object:

canvas.freeDrawingBrush.width = 3; canvas.freeDrawingBrush.color = '#1a1a2e';

Both properties are read fresh by the brush on every new stroke, so changing the range slider or clicking a color swatch mid-signature only affects strokes drawn *after* the change — already-committed paths keep their original width and color, which is the correct, expected behavior for a signature pad.

Exporting as PNG

canvas.toDataURL({ format: 'png', quality: 1, multiplier: 2 }) rasterizes the entire canvas — background color plus every drawn path — into a base64-encoded PNG data URL. The multiplier: 2 option is the detail worth calling out: Fabric renders the export at twice the on-screen resolution, internally scaling the canvas up before rasterizing and back down after, so the exported signature stays crisp even if it's later displayed larger than the pad itself (for example, printed on a document). Without a multiplier, the export is pixel-for-pixel identical to the visible canvas size, which can look soft when scaled up.

Clearing without losing the drawing surface

canvas.clear() removes every Path object Fabric has committed, but it also resets backgroundColor to Fabric's own default (transparent), which is why the clear handler explicitly re-sets canvas.backgroundColor afterward — otherwise the pad would turn transparent instead of returning to its paper tone.

Why a canvas-based pad beats raw <canvas> pointer code

Rolling this by hand means listening to pointerdown/pointermove/pointerup, buffering points, and drawing smoothed curves yourself — solvable, but Fabric's PencilBrush already does point-smoothing, and because strokes become real Path objects, you get undo (canvas.remove() on the last path), per-stroke styling, and JSON serialization for free, none of which raw canvas drawing gives you without extra code.

Step by step

How to Use

  1. 1
    Add the Fabric.js CDNInclude fabric.min.js from the CDN panel — it attaches a global fabric object.
  2. 2
    Paste HTML, CSS, and JSA ready-to-sign pad renders with a guide baseline.
  3. 3
    Sign with mouse or touchisDrawingMode routes pointer input straight into smoothed Path strokes.
  4. 4
    Adjust pen width and colorThe slider and swatches update freeDrawingBrush live for new strokes.
  5. 5
    Clear the padcanvas.clear() wipes all strokes; the background color is re-applied after.
  6. 6
    Export as PNGtoDataURL with multiplier: 2 renders a crisp, higher-resolution image preview.

Real-world uses

Common Use Cases

Contract and consent signing
Capture a legal or consent signature with smooth freehand strokes, using Fabric's free-drawing mode and a configurable PencilBrush.
Delivery sign-off pads
Provide a recipient sign-off for a courier app, with colour swatches and a Clear button for another attempt.
Quick sketch capture
Offer a minimal freehand input for annotations, where brush width and colour are read fresh at the start of every new stroke.
Canvas export learning
Learn how toDataURL with a multiplier of 2 rasterises a high-resolution PNG, doubling the on-screen resolution for crisp output.
Kiosk and tablet check-in
Support touch-friendly signing for in-person visits at a front desk, with multi-colour signing handled by swapping freeDrawingBrush.color only.

Got questions?

Frequently Asked Questions

Fabric does create a default free-drawing brush automatically, but explicitly constructing new fabric.PencilBrush(canvas) guarantees the brush type and gives an unambiguous, documented object to set width and color on, which keeps the code portable across Fabric versions.

No. freeDrawingBrush.width and .color are read when a new stroke begins, and each committed stroke becomes its own fabric.Path object with its own fixed styling, so past strokes are unaffected by later changes to the brush.

multiplier scales the rendered export resolution relative to the canvas\u2019s on-screen size. At 1 (the default) the export is pixel-for-pixel identical to the visible canvas, which can look soft if the image is later displayed larger. multiplier: 2 renders at double resolution for a crisper result.

clear() removes every canvas object AND resets background-related properties to Fabric\u2019s defaults, which is transparent. The clear handler explicitly re-applies the paper background color afterward so the pad does not turn transparent.

Check canvas.getObjects().length after drawing stops \u2014 free drawing commits each stroke as a Path object added to the canvas\u2019s object list, so zero objects means nothing was signed yet.

Yes \u2014 call canvas.toSVG() instead of toDataURL(). Because each stroke is already a Path object, Fabric serializes it as a real SVG <path> with an exact d attribute rather than rasterizing pixels.

Build with AI

Build, Understand, Optimize, and Extend It With AI

This snippet is small enough that the interesting questions are about export fidelity, not drawing mechanics. Paste the code into an AI assistant like Claude and ask it to explain exactly what canvas.toDataURL's multiplier option does internally \u2014 how Fabric scales the canvas up, rasterizes, and scales back down \u2014 and why that produces a crisper result than exporting at multiplier: 1 and upscaling the image afterward with CSS. Then ask how you'd detect an empty signature (hint: canvas.getObjects().length) to validate a form before submission. To extend it: add an SVG export path alongside the PNG one via canvas.toSVG(), add an undo button that pops the last object with canvas.remove(canvas.getObjects().slice(-1)[0]), support pressure-sensitive width on stylus input, or send the exported data URL to a backend endpoint as part of a real form submission.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a signature capture pad using Fabric.js (v5, from a CDN) in plain HTML, CSS, and JavaScript.

Requirements:
- A fabric.Canvas with isDrawingMode: true so pointer/touch input draws smoothed freehand strokes automatically, with a light paper-colored background and a subtle dashed baseline guide drawn in CSS (not on the canvas) hinting where to sign.
- Explicitly assign canvas.freeDrawingBrush = new fabric.PencilBrush(canvas) and set an initial width and color on it.
- A range slider that updates freeDrawingBrush.width live, and 3 color swatch buttons that update freeDrawingBrush.color live \u2014 changes should only affect strokes drawn after the change, not strokes already committed.
- A "Clear" button that calls canvas.clear() and then explicitly re-applies the background color afterward (since clear() resets it).
- An "Export as PNG" button that calls canvas.toDataURL({ format: 'png', quality: 1, multiplier: 2 }) to get a higher-resolution PNG data URL than the on-screen canvas size, and displays the result in a visible <img> preview below the pad.
- Style it as a clean light signing card inside a dark page shell, with the controls (width slider, color swatches, clear/export buttons) laid out in a row beneath the pad.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="fsp-stage">
  <div class="fsp-head">
    <span class="fsp-tag">Fabric.js · free drawing</span>
    <h2>Signature Pad</h2>
    <p>Sign below with mouse or touch. Adjust pen width and color, then export your signature as a PNG.</p>
  </div>
  <div class="fsp-pad">
    <canvas id="fspCanvas"></canvas>
    <div class="fsp-baseline"></div>
  </div>
  <div class="fsp-controls">
    <label class="fsp-field">
      <span>Pen width</span>
      <input type="range" id="fspWidth" min="1" max="10" value="3" />
    </label>
    <div class="fsp-colors" id="fspColors">
      <button class="fsp-swatch is-on" data-color="#1a1a2e" style="background:#1a1a2e"></button>
      <button class="fsp-swatch" data-color="#1e40af" style="background:#1e40af"></button>
      <button class="fsp-swatch" data-color="#7c2d12" style="background:#7c2d12"></button>
    </div>
    <div class="fsp-actions">
      <button class="fsp-btn fsp-btn-ghost" id="fspClear">Clear</button>
      <button class="fsp-btn" id="fspExport">Export as PNG</button>
    </div>
  </div>
  <img id="fspPreview" class="fsp-preview" alt="Exported signature preview" hidden />
</div>