How to generate html5 canvas drawing code from svg path data
- Step 1Drop in an SVG built from paths — Drag an
.svgonto the tool, or paste the source into the textarea. The exporter only reads<path>elements, so paths-only icons and logos convert cleanly. If your artwork uses<rect>,<circle>,<line>or<polygon>, flatten those to paths in your editor first (Illustrator: Object → Path; Figma: flatten/outline) — otherwise they are silently skipped. - Step 2Let it parse and translate each path — The tool walks every
<path>in document order. For each one it opens actx.beginPath(), converts thedattribute command-by-command, then appends a fill and/or stroke based on the path's ownfill/strokeattributes. Everything is wrapped once inctx.save()…ctx.restore()so it doesn't leak state into the rest of your canvas. - Step 3Note the function name it picked — The export name is derived from the file name:
search-icon.svgbecomesexport function drawSearchIcon(ctx). Pasted source is treated asinput.svg, so it becomesdrawInput. Rename the function after copying if you want something different — the name is cosmetic, the geometry is what matters. - Step 4Review the output for dropped or degraded parts — Scan the generated JS. Any elliptical arc (
A/a) shows a// arc approximated as line — review and adjust if precise arc geometry is requiredcomment followed by a plainctx.lineTo(). If a path you expected is missing entirely, it either wasn't a<path>or its geometry used only ignored commands. There is no gradient, text, or clip-path output. - Step 5Copy or download the module — Use Copy to clipboard for a quick paste, or Download to save
<name>-canvas.js. Because it is alreadyexport function …, you canimport { drawSearchIcon } from './search-icon-canvas.js'with no edits in any bundler or native-ESM page. - Step 6Call it inside your render code — Get a 2D context and call the function:
const ctx = canvas.getContext('2d'); drawSearchIcon(ctx);. To position or scale the shape, set up the transform yourself before the call — e.g.ctx.translate(x, y); ctx.scale(s, s); drawSearchIcon(ctx);— because the generated function takes onlyctx.
SVG path command → Canvas 2D call
Exactly how each path command is translated, verified against the exporter's parser. Relative (lowercase) commands are resolved to absolute coordinates before the call is written.
| SVG command | Canvas call emitted | Notes |
|---|---|---|
M / m | ctx.moveTo(x, y) | Sets the current and subpath-start point. Extra coordinate pairs after an M are treated as implicit L (line) commands, per the SVG spec |
L / l | ctx.lineTo(x, y) | Straight segment to the new point |
H / h | ctx.lineTo(x, currentY) | Horizontal line — y is held at the current point's y |
V / v | ctx.lineTo(currentX, y) | Vertical line — x is held at the current point's x |
C / c | ctx.bezierCurveTo(x1,y1,x2,y2,x,y) | Cubic Bézier, control points passed through directly |
S / s | ctx.bezierCurveTo(…) | Smooth cubic — the first control point is reflected from the previous cubic's second control point automatically |
Q / q | ctx.quadraticCurveTo(x1,y1,x,y) | Quadratic Bézier |
T / t | ctx.quadraticCurveTo(…) | Smooth quadratic — control point reflected from the previous quadratic automatically |
A / a | // arc approximated as line + ctx.lineTo(x,y) | Degraded: only the endpoint is used. rx, ry, rotation, large-arc and sweep flags are discarded. Not ctx.arc, not a Bézier approximation |
Z / z | ctx.closePath() | Closes the subpath and returns the current point to the subpath start |
What is exported vs. ignored
The exporter is a path translator, not a full SVG renderer. This is the precise scope — anything not listed as exported is absent from the output.
| SVG feature | In the output? | What actually happens |
|---|---|---|
<path> geometry | Exported | Each path becomes a beginPath() + command calls block |
fill attribute on a path | Exported | If present and not none: ctx.fillStyle = '<value>'; ctx.fill(); |
stroke attribute on a path | Exported | If present and not none: ctx.strokeStyle = '<value>'; ctx.stroke(); |
<rect> <circle> <ellipse> <line> <polygon> <polyline> | Ignored | Not <path> elements, so they are skipped silently — convert them to paths first |
<text> | Ignored | No ctx.fillText() is generated. Outline the text to paths (try svg-font-to-path) first |
Gradients (linearGradient/radialGradient) | Ignored | No createLinearGradient/createRadialGradient is produced; the path falls back to its solid fill/stroke attribute if any |
| Filters, clip-paths, masks | Ignored | No Canvas equivalent is emitted |
fill-opacity, opacity, stroke-width, inline style=, CSS classes | Ignored | Only the literal fill and stroke attributes on the path are read |
Cookbook
Real before/after pairs from real conversions — the left side is the SVG you drop in, the right side is the generated Canvas code, verbatim from the exporter's logic.
A triangle (M + L + Z) with a fill
The simplest case: a closed polyline with one fill colour. This is exactly what the exporter is best at.
Input (triangle.svg):
<svg viewBox="0 0 100 100">
<path d="M10 90 L50 10 L90 90 Z" fill="#6366f1" />
</svg>
Output (triangle-canvas.js):
// Generated by JAD SVG Canvas Exporter
// Usage: pass a CanvasRenderingContext2D as ctx
export function drawTriangle(ctx) {
ctx.save();
// Path 1
ctx.beginPath();
ctx.moveTo(10, 90);
ctx.lineTo(50, 10);
ctx.lineTo(90, 90);
ctx.closePath();
ctx.fillStyle = '#6366f1'; ctx.fill();
ctx.restore();
}A cubic Bézier with both fill and stroke
When a path carries both a fill and a stroke attribute, the exporter writes both — fill first, then stroke — exactly as the painter's order in SVG.
Input:
<path d="M20 80 C20 20 80 20 80 80"
fill="#fde68a" stroke="#1f2937" />
Output function body:
// Path 1
ctx.beginPath();
ctx.moveTo(20, 80);
ctx.bezierCurveTo(20, 20, 80, 20, 80, 80);
ctx.fillStyle = '#fde68a'; ctx.fill();
ctx.strokeStyle = '#1f2937'; ctx.stroke();Smooth shorthand (S) gets its control point reflected for you
S omits the first control point, which the spec says you reflect from the previous cubic's second control point. The exporter computes that reflection so you don't have to.
Input: <path d="M10 50 C30 10 50 10 70 50 S110 90 130 50" fill="none" stroke="#10b981" /> Output: ctx.beginPath(); ctx.moveTo(10, 50); ctx.bezierCurveTo(30, 10, 50, 10, 70, 50); // S reflected: first control point = 2*current - prev2nd = (90, 90) ctx.bezierCurveTo(90, 90, 110, 90, 130, 50); ctx.strokeStyle = '#10b981'; ctx.stroke(); (fill="none" → no fill() call is written)
An arc (A) is downgraded to a straight line
Canvas 2D has no endpoint-notation elliptical arc, so the exporter does not attempt one. It draws a lineTo to the arc's endpoint and flags it with a comment for you to fix if the curve matters.
Input: <path d="M10 50 A40 40 0 0 1 90 50" fill="none" stroke="#000" /> Output: ctx.beginPath(); ctx.moveTo(10, 50); // arc approximated as line — review and adjust if precise arc geometry is required ctx.lineTo(90, 50); ctx.strokeStyle = '#000'; ctx.stroke(); → For real arc geometry, replace with ctx.arc()/ctx.ellipse() by hand, or convert the arc to cubic Béziers in your editor before exporting.
Calling the exported function with a transform
Because draw<Name>(ctx) takes only the context, positioning and scaling are done by transforming the context first. This is the standard pattern for reusing one shape at many places.
import { drawTriangle } from './triangle-canvas.js';
const ctx = document.querySelector('canvas').getContext('2d');
// draw it small, top-left
ctx.save();
ctx.translate(0, 0); ctx.scale(0.5, 0.5);
drawTriangle(ctx);
ctx.restore();
// draw it big, centred
ctx.save();
ctx.translate(200, 150); ctx.scale(2, 2);
drawTriangle(ctx);
ctx.restore();Edge cases and what actually happens
SVG has no `<path>` elements (all `<rect>`/`<circle>`)
Empty bodyThe exporter selects <path> elements only. A file made entirely of <rect>, <circle>, <line> or <polygon> produces a valid function with nothing between ctx.save() and ctx.restore(). Fix: flatten primitives to paths in your vector editor (Illustrator Object → Path, Inkscape Path → Object to Path, Figma flatten) before exporting.
Path uses an elliptical arc (`A`/`a`)
Degraded to lineThe arc's radii, x-rotation, and the large-arc/sweep flags are discarded; only the endpoint survives as a ctx.lineTo(). A rounded corner or a circular arc becomes a straight chord. A comment marks every occurrence so you can find and hand-fix them. If arcs are important, convert them to cubic Béziers in your editor first.
Colour is defined in CSS or inline `style=`, not the `fill` attribute
Color droppedThe exporter reads only the literal fill and stroke attributes on each <path>. A path coloured via a style="fill:#f00" declaration, a CSS class, or a parent <g fill> produces no fillStyle/fill() call. Move the colour onto the path's fill/stroke attribute before exporting, or set ctx.fillStyle yourself after the generated beginPath() block.
Gradient fill (`fill="url(#grad)"`)
Not supportedGradients are not translated. A path with fill="url(#myGradient)" will emit ctx.fillStyle = 'url(#myGradient)'; ctx.fill(); — which Canvas cannot resolve, so nothing visible is filled. Replace it with a real ctx.createLinearGradient(...)/createRadialGradient(...) by hand, or flatten the gradient to a solid colour before export.
Stroke width / dash / line cap matter
Width ignoredOnly the stroke colour is carried over via ctx.strokeStyle. stroke-width, stroke-linecap, stroke-linejoin, and stroke-dasharray are not read, so the generated stroke uses Canvas defaults (1px, butt cap, miter join). Set ctx.lineWidth, ctx.lineCap, etc. yourself before calling the draw function.
Text element in the SVG
Not supported<text> is ignored entirely — no ctx.fillText() is produced. If you need the lettering as geometry, outline it to paths first with svg-font-to-path (which needs the font URL), then run the resulting path-only SVG through this exporter.
Path data has malformed or unknown command tokens
Token skippedThe parser tokenises commands and numbers; an unrecognised token is skipped rather than throwing. Severely malformed d strings can therefore produce an incomplete shape. If the output geometry looks wrong, validate the SVG (the tool rejects non-SVG paste with an Invalid SVG error) and clean the path data first.
Whole-SVG transform on `<svg>` or a wrapping `<g>`
Transform droppedA transform on the root or a group is not baked into the coordinates — the exporter reads each path's raw d values. If your artwork relies on a group transform for position/scale, apply (flatten) those transforms in your editor first, or reproduce them with ctx.translate/ctx.scale/ctx.rotate before calling the function.
Tool shows an upgrade overlay instead of running
Developer tierThe Canvas Exporter is gated to the Developer plan. On Free or Pro you'll see the plan overlay rather than the upload box. The path-to-canvas translation is the Developer-tier feature; the file-size cap shown in the dropzone (5 MB Free, 50 MB Pro, 2 GB otherwise) is separate from the tier gate.
Frequently asked questions
What exactly does the generated function look like?
A single ES module export: export function draw<Name>(ctx) { ctx.save(); /* per-path beginPath + commands + fill/stroke */ ctx.restore(); }. The name comes from the file name (logo.svg → drawLogo). It takes one argument — the CanvasRenderingContext2D — and returns nothing; it just issues drawing calls against the context you pass in.
Does the function take x, y, and scale parameters?
No. The signature is draw<Name>(ctx) — context only. To position or scale, transform the context yourself first: ctx.save(); ctx.translate(x, y); ctx.scale(s, s); drawLogo(ctx); ctx.restore();. The shape draws at its original SVG coordinates relative to whatever transform is active.
Which SVG path commands are supported?
M L H V C S Q T Z (and their relative lowercase forms) map cleanly to Canvas calls — moveTo, lineTo, bezierCurveTo, quadraticCurveTo, closePath — with control-point reflection handled for smooth S/T. The elliptical-arc command A/a is the exception: it is approximated by a straight line to the endpoint, with a comment flagging it.
Why are my rectangles and circles missing from the output?
The exporter processes <path> elements only. <rect>, <circle>, <ellipse>, <line>, <polygon> and <polyline> are skipped. Convert them to paths in your vector editor first (most editors have an 'Object to Path' / 'Flatten' command), then re-export.
Does it support SVG gradients on canvas?
No. There is no createLinearGradient/createRadialGradient output, and a fill="url(#grad)" will not render. Flatten gradients to a solid colour before exporting, or add the Canvas gradient calls by hand after the generated beginPath() block.
How are fill and stroke colours handled?
The exporter copies each path's literal fill and stroke attribute into ctx.fillStyle/ctx.fill() and ctx.strokeStyle/ctx.stroke(). fill="none" or stroke="none" suppresses that call. Colours defined via CSS, a style= attribute, or an inherited group are not read.
What happens to elliptical arcs (the A command)?
Canvas 2D has no direct endpoint-notation arc, so the exporter draws a straight ctx.lineTo() to the arc's endpoint and marks it with // arc approximated as line. For accurate geometry, convert arcs to cubic Béziers in your editor before exporting, or replace the line with a hand-written ctx.arc()/ctx.ellipse().
Can I use the output in WebGL?
No — the generated code targets CanvasRenderingContext2D, the 2D API. WebGL needs vertex buffers and shaders, a completely different model. You could sample the path geometry into vertex arrays yourself, but the exporter does not produce WebGL output.
Is the output really an ES module I can import?
Yes. It starts with export function …, so import { drawLogo } from './logo-canvas.js' works directly in any bundler (Vite, Webpack, Rollup, esbuild) or a native <script type="module"> page — no wrapper or post-processing needed.
Is my SVG uploaded to a server?
No. Parsing and translation happen in your browser via the DOM parser; the result panel shows a 0 bytes uploaded badge. The SVG content never leaves your machine.
Is this a Developer-tier feature?
Yes. The Canvas Exporter is gated to the Developer plan. On Free/Pro you'll see an upgrade overlay instead of the tool. There are no configuration options to set — you drop in an SVG and copy the generated module.
What's the difference between this and the React/Vue exporters?
svg-to-jsx and svg-to-vue-svelte keep the SVG as declarative markup wrapped in a component — the browser still renders it. The Canvas Exporter throws away the SVG DOM and gives you imperative drawing calls for a <canvas>. Use the component exporters for UI icons; use this when you need to paint into a canvas yourself.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.