Written: 2026-03-30, Updated: 2026-04-06
This document is a short explainer for an implementation of an existing consensus standard (SVG Paths §7 DOM Interfaces). 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.
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 W3C Editor's Draft and already shipped in Firefox 137+ (Apr 2025).
SVG <path> elements define their shape through a d attribute string. Today in Chrome, the only way to inspect or modify individual path segments is to parse this raw string manually or include a polyfill. This can result in slower interactions, larger page loads, and degraded UX - especially on low-end devices.
Chromium removed the old SVGPathSegList API in Chrome 48 (Jan 2016) because it was overly complex and poorly specified. The SVG WG specified a cleaner replacement, but it has not yet been implemented in Chrome - a gap that has persisted for over 10 years.
| Engine | Old API (SVGPathSegList) |
New API (getPathData/setPathData) |
|---|---|---|
| Chrome | ❌ Removed Jan 2016 | ❌ Not implemented |
| Firefox | ❌ Removed 2018 | ✅ Shipped Apr 2025 |
| Safari | ✅ Still supported | ❌ Not implemented |
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).
Current workarounds: path-data-polyfill (129+ stars, de facto standard), manual d string parsing, and pathseg polyfill. All are slower than native and add unnecessary JS weight.
Developer demand: crbug.com/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.
getPathData({normalize: true}) returns only absolute M, L, C, Z.SVGPathSegList - the old API is not being brought back.R) or Bearing (B); no browser supports them.SVGPathSegment constructor - the SVG WG resolved that a constructor is not needed at this time; our dictionary approach aligns with this.Three methods are added to SVGPathElement, using simple {type, values} plain objects (no dependencies on non-stable features):
getPathData(settings) - read segmentsconst segments = path.getPathData();
// → [{type: "M", values: [10, 80]}, {type: "C", values: [40, 10, 65, 10, 95, 80]}, ...]
// Normalize: all segments converted to absolute M, L, C, Z
const normalized = path.getPathData({normalize: true});
// Empty or missing d attribute returns an empty array
emptyPath.getPathData(); // → []
setPathData(pathData) - write segments (accepts POJOs)path.setPathData([
{type: "M", values: [0, 0]},
{type: "L", values: [100, 0]},
{type: "L", values: [50, 100]},
{type: "Z", values: []}
]);
// Passing an empty array clears the path: sets d="" (equivalent to setAttribute('d', ''),
// NOT removeAttribute('d') - the attribute remains present but empty). Matches Firefox.
path.setPathData([]);
// getPathData() on an empty/cleared path returns []
emptyPath.getPathData(); // → []
getPathSegmentAtLength(distance) - segment at distancepath.getPathSegmentAtLength(50);
// → {type: "C", values: [40, 10, 65, 10, 95, 80]}
// Returns null if the path is empty or has no length
emptyPath.getPathSegmentAtLength(10); // → null
// Negative distances clamp to 0 (returns the first segment), matching getPointAtLength() behavior
path.getPathSegmentAtLength(-10); // → {type: "M", values: [10, 80]}
// NaN returns null
path.getPathSegmentAtLength(NaN); // → null
// Distances exceeding getTotalLength() clamp to the path's total length (returns last segment),
// matching getPointAtLength() clamping behavior per the SVG spec.
path.getPathSegmentAtLength(99999); // → last segment (e.g. {type: "Z", values: []})
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 for the full type/values mapping.
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.
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.
// BEFORE: parse d-string manually or include a polyfill
const d = path.getAttribute('d');
const segments = myCustomParser(d); // or load ~4KB polyfill
segments[1].values[0] = 50;
path.setAttribute('d', myCustomSerializer(segments));
// AFTER: native, zero dependencies
const segments = path.getPathData();
segments[1].values[0] = 50;
path.setPathData(segments);
The formal WebIDL is in the Appendix.
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. Firefox initially required interface instances (Firefox 137), which caused polyfill compatibility issues, and later updated to accept plain objects in Firefox 138. Using a dictionary from the start avoids this.
unrestricted float for values. NaN/Infinity are accepted without throwing, matching SVG's graceful error model and Firefox's behavior.
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.
Returns base value, not animated value. getPathData() returns the d attribute's base value, consistent with getAttribute('d') and Firefox.
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).
Our implementation-specific choices (dictionary vs interface, unrestricted float, lenient validation) are documented in Key Design Decisions above.
getAttribute('d') - purely a convenience API over existing capabilities. No fingerprinting surface, no network requests.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).| Stakeholder | Signal | Evidence |
|---|---|---|
| Firefox | ✅ Positive | Shipped Firefox 137 (Apr 2025); POJO fix in 138 |
| Safari/WebKit | No signal | Still ships old API; removal bug open (TODO: file WebKit standards position request) |
| Web developers | ✅ Strongly positive | 45 upvotes, 31 comments, enterprise breakage reports, 129+ polyfill stars |
| SVG WG | ✅ Positive | API in consensus spec; dictionary approach confirmed in w3c/svgwg#1082 |
Specs: SVG Paths · SVG Paths §7 DOM Interfaces · SVG 2
Bugs: Chromium 40441025 · Firefox 1934525 · Firefox 1954044 · WebKit 260894
Discussions: w3c/editing#483 · w3c/svgwg#974 · w3c/svgwg#1082 (dictionary resolution)
Prior art: path-data-polyfill (129+ stars) · pathseg polyfill · Interop hotlist (Chromium cross-browser interop tracking; includes crbug.com/40441025)
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).
Existing WPTs: Firefox landed web-platform-tests alongside their implementation in svg/path/interfaces/, including SVGPathSegment.svg which covers getPathData(), setPathData(), getPathSegmentAtLength(), normalization, and basic command coverage.
Planned additional tests:
{type, values} objects work without constructorsFeature 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).
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.
dictionary SVGPathSegment {
required DOMString type;
required sequence<unrestricted float> values;
};
dictionary SVGPathDataSettings {
boolean normalize = false;
};
partial interface SVGPathElement {
sequence<SVGPathSegment> getPathData(optional SVGPathDataSettings settings = {});
undefined setPathData(sequence<SVGPathSegment> pathData);
SVGPathSegment? getPathSegmentAtLength(unrestricted float distance);
};
Spec text updates (spec PR to be filed): dictionary instead of [NoInterfaceObject] interface (accepts POJOs natively; WG resolution: w3c/svgwg#1082); unrestricted float instead of float (matches SVG error model); required keywords added (prevents setPathData([{}])).