You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: SVG/SVGPathDataAPI/explainer.md
+53-53Lines changed: 53 additions & 53 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,14 @@
1
1
# SVG Path Data API
2
2
3
-
**Written:** 2026-03-30, **Updated:** 2026-03-30
3
+
**Written:** 2026-03-30, **Updated:** 2026-04-06
4
4
5
5
## Authors
6
6
7
7
- Virali Purbey (viralipurbey@microsoft.com)
8
8
9
9
## Status of this Document
10
10
11
-
This document is an**in-progress** explainer.
11
+
This document is a**short explainer**for an implementation of an existing consensus standard ([SVG Paths §7 DOM Interfaces](https://svgwg.org/specs/paths/#DOMInterfaces)). No new web platform concepts are introduced. This explainer captures developer benefit, key implementation decisions, and Chromium-specific shipping details; it is intentionally concise since the API was designed by the SVG WG, not the authors of this document.
12
12
13
13
## Participate
14
14
@@ -24,22 +24,21 @@ This document is an **in-progress** explainer.
Chrome has had **no native way** to read or write individual SVG path segments since 2015. This explainer proposes adding `getPathData()`, `setPathData()`, and `getPathSegmentAtLength()` to `SVGPathElement`, giving developers structured access to path segments as simple `{type, values}` objects.
41
-
42
-
The API is specified in the [SVG Paths](https://svgwg.org/specs/paths/#DOMInterfaces) W3C Editor's Draft and has shipped in **Firefox 137+** (Jan 2025). This implements an existing consensus standard - no new web platform concepts are introduced.
41
+
Chrome has had **no native way** to read or write individual SVG path segments since Chrome 48 (early 2016). This explainer proposes implementing `getPathData()`, `setPathData()`, and `getPathSegmentAtLength()` on `SVGPathElement`, as specified in the [SVG Paths](https://svgwg.org/specs/paths/#DOMInterfaces) W3C Editor's Draft and already shipped in **Firefox 137+** (Apr 2025).
43
42
44
43
---
45
44
@@ -51,14 +50,16 @@ Chromium removed the old `SVGPathSegList` API in **Chrome 48** (Jan 2016) becaus
51
50
52
51
| Engine | Old API (`SVGPathSegList`) | New API (`getPathData`/`setPathData`) |
| Chrome | ❌ Removed Jan 2016 | ❌ Not implemented|
54
+
| Firefox | ❌ Removed 2018 | ✅ Shipped Apr 2025 |
55
+
| Safari | ✅ Still supported | ❌ Not implemented|
57
56
58
57
**Who is affected:** End users of SVG-heavy web apps (slower load times due to polyfills); data visualization developers (D3.js path morphing); SVG editor developers (Boxy SVG, SVG-Edit); animation developers (path interpolation).
59
58
60
59
**Current workarounds:**[path-data-polyfill](https://github.com/jarek-foksa/path-data-polyfill) (129+ stars, de facto standard), manual `d` string parsing, and [pathseg polyfill](https://github.com/progers/pathseg). All are slower than native and add unnecessary JS weight.
61
60
61
+
**Developer demand:**[crbug.com/40441025](https://issues.chromium.org/issues/40441025) has **45 upvotes** and **31 comments** from enterprise developers and library authors over 10 years. Five Sheriffbot closure attempts (2017-2021) were each reopened.
62
+
62
63
---
63
64
64
65
## Goals
@@ -78,21 +79,9 @@ Chromium removed the old `SVGPathSegList` API in **Chrome 48** (Jan 2016) becaus
78
79
79
80
---
80
81
81
-
## User Research
82
-
83
-
No formal study was conducted, but 10 years of organic feedback on [crbug.com/40441025](https://issues.chromium.org/issues/40441025) provides helpful signal: **45 upvotes**, **31 comments** (enterprise developers, library authors), **129+ GitHub stars** on the polyfill, and **5 Sheriffbot closure attempts** survived (2017-2021, each reopened by fs@opera.com).
84
-
85
-
> *"In our B2B solution for glasses design we have round about 1000 users which can not work since the last Google Chrome update."* - Jan 2016
86
-
87
-
> *"We'll soon celebrate the 10th anniversary of this issue. It's... a long time."* - Jun 2025
88
-
89
-
---
90
-
91
82
## Proposed Approach
92
83
93
-
**Dependencies on non-stable features:** None.
94
-
95
-
Three methods are added to `SVGPathElement`, using simple `{type, values}` plain objects:
84
+
Three methods are added to `SVGPathElement`, using simple `{type, values}` plain objects (no dependencies on non-stable features):
96
85
97
86
#### `getPathData(settings)` - read segments
98
87
@@ -117,8 +106,11 @@ path.setPathData([
117
106
{type:"Z", values: []}
118
107
]);
119
108
120
-
// Passing an empty array clears the path (equivalent to setAttribute('d', ''))
109
+
// Passing an empty array clears the path: sets d="" (equivalent to setAttribute('d', ''),
110
+
// NOT removeAttribute('d') - the attribute remains present but empty). Matches Firefox.
121
111
path.setPathData([]);
112
+
// getPathData() on an empty/cleared path returns []
113
+
emptyPath.getPathData(); // → []
122
114
```
123
115
124
116
#### `getPathSegmentAtLength(distance)` - segment at distance
// Distances exceeding getTotalLength() clamp to the path's total length (returns last segment),
132
+
// matching getPointAtLength() clamping behavior per the SVG spec.
133
+
path.getPathSegmentAtLength(99999); // → last segment (e.g. {type: "Z", values: []})
138
134
```
139
135
140
136
All 20 SVG path commands (M, m, L, l, H, h, V, v, C, c, S, s, Q, q, T, t, A, a, Z, z) are supported. See the [spec](https://svgwg.org/specs/paths/#DOMInterfaces) for the full type/values mapping.
141
137
142
138
**Normalization** (`{normalize: true}`) converts all segments to absolute **M, L, C, Z** only - relative to absolute, H/V to L, Q/T to C, S to C, A to C. Consumers need only handle 4 command types.
143
139
144
-
> **Note:** Arc-to-cubic conversion (A → C) is an approximation using midpoint subdivision and is inherently lossy. The precision matches the existing `getTotalLength()`/`getPointAtLength()` code path in Blink. For most use cases the approximation error is sub-pixel.
140
+
> **Note:** Arc-to-cubic conversion (A → C) is an approximation (inherently lossy); quadratic-to-cubic (Q → C) is exact. Precision details will be in the design doc.
145
141
146
142
### Before and after
147
143
@@ -160,43 +156,27 @@ segments[1].values[0] = 50;
160
156
path.setPathData(segments);
161
157
```
162
158
163
-
### Example: path morphing
164
-
165
-
```js
166
-
constsegA=pathA.getPathData({normalize:true});
167
-
constsegB=pathB.getPathData({normalize:true});
168
-
constinterpolate= (t) =>segA.map((s, i) => ({
169
-
type:s.type,
170
-
values:s.values.map((v, j) => v + (segB[i].values[j] - v) * t)
171
-
}));
172
-
pathTarget.setPathData(interpolate(0.5));
173
-
```
174
-
175
159
The formal WebIDL is in the [Appendix](#appendix-webidl).
176
160
177
161
---
178
162
179
163
## Key Design Decisions
180
164
181
-
1.**Plain objects, not class instances.** We use a WebIDL `dictionary`, so `setPathData()` accepts plain `{type, values}` POJOs natively. Firefox initially required interface instances (Firefox 137), which caused polyfill compatibility issues, and later [updated](https://bugzilla.mozilla.org/show_bug.cgi?id=1954044) to accept plain objects in Firefox 138. Using a dictionary from the start avoids this.
165
+
1.**Plain objects, not class instances.** We use a WebIDL `dictionary`, so `setPathData()` accepts plain `{type, values}` POJOs natively. The SVG WG confirmed this approach in [w3c/svgwg#1082](https://github.com/w3c/svgwg/issues/1082). Firefox initially required interface instances (Firefox 137), which caused polyfill compatibility issues, and later [updated](https://bugzilla.mozilla.org/show_bug.cgi?id=1954044) to accept plain objects in Firefox 138. Using a dictionary from the start avoids this.
182
166
183
167
2.**`unrestricted float` for values.** NaN/Infinity are accepted without throwing, matching SVG's graceful error model and Firefox's behavior.
184
168
185
-
3.**Invalid segments silently skipped.**Unrecognized types or wrong value counts in `setPathData()`are skipped (not thrown), matching SVG's "render what you can" model, Firefox, and the polyfill.
169
+
3.**Two-level validation in `setPathData()`.**WebIDL enforces structural validity: both `type` and `values` must be present, or a `TypeError` is thrown (e.g., `setPathData([{}])`or `setPathData([{type: "L"}])` throws). Semantic validation is lenient: unrecognized type strings or incorrect `values` array lengths cause the segment to be silently skipped - not thrown - matching SVG's "render what you can" model, Firefox, and the polyfill.
186
170
187
171
4.**Returns base value, not animated value.**`getPathData()` returns the `d` attribute's base value, consistent with `getAttribute('d')` and Firefox.
188
172
189
173
---
190
174
191
175
## Alternatives Considered
192
176
193
-
| Alternative | Why rejected |
194
-
|---|---|
195
-
|**Re-implement `SVGPathSegList`**| SVG WG removed it from SVG 2; live mutation is complex; 20+ factory methods; no modern engine adding new support ([WebKit removal bug](https://bugs.webkit.org/show_bug.cgi?id=260894)) |
196
-
|**Use `interface` per spec text**| Would not accept plain objects from polyfill code; Firefox encountered this and updated to accept POJOs; spec author confirmed dictionary was the intent |
197
-
|**Use `float` (not `unrestricted`)**| SVG renders degenerate paths as empty rather than erroring; would affect polyfill-based code; Firefox uses unrestricted |
198
-
|**Throw on invalid segments**| Firefox and polyfill skip silently; SVG model is "render what you can" |
199
-
|**Return animated value**| No use case identified; adds complexity; inconsistent with `getAttribute('d')`; Firefox returns base |
177
+
The API shape was designed by the SVG WG, not the authors of this document. The main alternative - re-implementing the old `SVGPathSegList` API - was rejected by the WG because of its complexity (20+ factory methods, live mutation semantics). No modern engine is adding new `SVGPathSegList` support ([WebKit removal bug](https://bugs.webkit.org/show_bug.cgi?id=260894)).
178
+
179
+
Our implementation-specific choices (dictionary vs interface, `unrestricted float`, lenient validation) are documented in [Key Design Decisions](#key-design-decisions) above.
200
180
201
181
---
202
182
@@ -205,19 +185,18 @@ The formal WebIDL is in the [Appendix](#appendix-webidl).
205
185
-**Accessibility:** No impact. Programmatic API only - no new visual content, interaction patterns, or ARIA roles. Indirectly benefits a11y by making it easier to build well-structured SVG.
206
186
-**Internationalization:** No impact. Path data uses single-character Latin commands and numbers only.
207
187
-**Privacy:** No new concerns. Returns the same data available via `getAttribute('d')` - purely a convenience API over existing capabilities. No fingerprinting surface, no network requests.
208
-
-**Security:** No new concerns. Operates entirely within the renderer on already-structured data. No string parsing is needed (segments are pre-typed), reducing attack surface compared to `setAttribute('d')`. No IPC. Gated behind a feature flag.
188
+
-**Security:** No new concerns. Operates entirely within the renderer on already-structured data. `setPathData()` operates on structured `{type, values}` dictionaries - no string parsing is needed (segments are pre-typed), reducing attack surface compared to `setAttribute('d')`. No additional IPC beyond existing DOM access. Gated behind a Blink `RuntimeEnabledFeature` (`SVGPathDataAPI`).
|**Safari/WebKit**| No signal | Still ships old API; [removal bug](https://bugs.webkit.org/show_bug.cgi?id=260894) open (TODO: file WebKit standards position request) |
|**SVG WG**| ✅ Positive | API in [consensus spec](https://svgwg.org/specs/paths/#DOMInterfaces); dictionary approach confirmed in [w3c/svgwg#1082](https://github.com/w3c/svgwg/issues/1082)|
221
200
222
201
---
223
202
@@ -227,14 +206,35 @@ The formal WebIDL is in the [Appendix](#appendix-webidl).
**Acknowledgements:** Fredrik Söderquist (fs@opera.com, original API sketch author, SVG OWNERS), Philip Rogers (pdr@chromium.org, drove SVGPathSegList removal, pathseg polyfill), Robert Longson (Mozilla SVG lead, Firefox implementation), Jarek Foksa (path-data-polyfill author), Cameron McCormack (spec editor).
235
214
236
215
---
237
216
217
+
## Testing
218
+
219
+
**Existing WPTs:** Firefox landed web-platform-tests alongside their implementation in [svg/path/interfaces/](https://wpt.fyi/results/svg/path/interfaces?label=experimental&label=master&aligned), including `SVGPathSegment.svg` which covers `getPathData()`, `setPathData()`, `getPathSegmentAtLength()`, normalization, and basic command coverage.
- POJO acceptance: plain `{type, values}` objects work without constructors
225
+
- Two-level validation: TypeError for missing required fields vs silent skip for semantic errors
226
+
- Blink layout tests for rendering integration
227
+
228
+
---
229
+
230
+
## Implementation Notes
231
+
232
+
**Feature flag:** This API will be gated behind a Blink `RuntimeEnabledFeature` named `SVGPathDataAPI`. It will not have a separate `chrome://flags` entry - it follows the standard Blink shipping process (flag → origin trial → ship).
233
+
234
+
**UseCounters:** The implementation will include UseCounters for each method (`getPathData`, `setPathData`, `getPathSegmentAtLength`) to track adoption and inform the ship decision. No existing UseCounter data is available since the API does not yet exist in Blink.
0 commit comments