@emasoft/svg-matrix
v1.3.15
Published
Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js
Maintainers
Readme
@emasoft/svg-matrix
What Is This?
This package contains two libraries that work together:
| Library | Purpose | Precision | |---------|---------|-----------| | Core Math | Vectors, matrices, 2D/3D transforms | 80 digits (configurable to 10^9) | | SVG Toolbox | Parse, transform, validate, optimize SVG files | 80 digits + visual verification |
Think of it like this:
Core Math is a calculator that never makes rounding errors. SVG Toolbox uses that calculator to work with SVG graphics perfectly.
Part 1: Core Math Library
For: Scientists, engineers, game developers, anyone who needs exact calculations.
What Can It Do?
Imagine you want to rotate a spaceship in a game, or calculate where two laser beams cross. Normal JavaScript math has tiny errors that add up. This library has zero errors because it uses 80-digit precision.
// Normal JavaScript: 0.1 + 0.2 = 0.30000000000000004 (wrong!)
// svg-matrix: 0.1 + 0.2 = 0.3 (exactly right)Vectors (Arrows in Space)
A vector is like an arrow pointing somewhere. You can add arrows, measure them, find angles between them.
import { Vector } from '@emasoft/svg-matrix';
// Create an arrow pointing right 3 units and up 4 units
const arrow = Vector.from([3, 4]);
// How long is the arrow? (It's 5 - like a 3-4-5 triangle!)
console.log(arrow.norm().toString()); // "5"
// Make it exactly 1 unit long (normalize)
const unit = arrow.normalize();
console.log(unit.toNumberArray()); // [0.6, 0.8]Matrices (Grids of Numbers)
A matrix is a grid of numbers. You can multiply them, flip them, use them to solve puzzles.
import { Matrix } from '@emasoft/svg-matrix';
// Create a 2x2 grid
const grid = Matrix.from([
[4, 7],
[2, 6]
]);
// Find the determinant (a special number about the grid)
console.log(grid.determinant().toString()); // "10"
// Solve a puzzle: find x and y where 4x + 7y = 1 and 2x + 6y = 0
const answer = grid.solve([1, 0]);
console.log(answer.toNumberArray()); // [0.6, -0.2]Transforms (Moving & Spinning Things)
Transforms move, rotate, scale, or skew shapes. This is how video games move characters around!
import { Transforms2D } from '@emasoft/svg-matrix';
// Move something 100 pixels right
const move = Transforms2D.translation(100, 0);
// Spin something 45 degrees
const spin = Transforms2D.rotate(Math.PI / 4);
// Make something twice as big
const grow = Transforms2D.scale(2);
// Apply spin to a point at (10, 0)
const [x, y] = Transforms2D.applyTransform(spin, 10, 0);
console.log(x.toFixed(4), y.toFixed(4)); // "7.0711 7.0711"Core Math API Quick Reference
Vector
| Method | What It Does |
|--------|--------------|
| Vector.from([x, y, z]) | Create a vector |
| .add(v) | Add two vectors |
| .sub(v) | Subtract vectors |
| .scale(n) | Multiply by a number |
| .dot(v) | Dot product (single number result) |
| .cross(v) | Cross product (3D only) |
| .norm() | Length of the vector |
| .normalize() | Make length = 1 |
| .angleBetween(v) | Angle between two vectors |
| .toNumberArray() | Convert to regular JavaScript array |
Matrix
| Method | What It Does |
|--------|--------------|
| Matrix.from([[...], [...]]) | Create from 2D array |
| Matrix.identity(n) | Identity matrix (1s on diagonal) |
| Matrix.zeros(r, c) | Matrix of zeros |
| .mul(M) | Multiply matrices |
| .transpose() | Flip rows and columns |
| .determinant() | Calculate determinant |
| .inverse() | Calculate inverse |
| .solve(b) | Solve system of equations |
| .lu() | LU decomposition |
| .qr() | QR decomposition |
Transforms2D / Transforms3D
| Method | What It Does |
|--------|--------------|
| translation(x, y) | Move transform |
| scale(sx, sy) | Size transform |
| rotate(angle) | Spin transform (radians) |
| rotateAroundPoint(angle, px, py) | Spin around a specific point |
| skew(ax, ay) | Slant transform |
| reflectX() / reflectY() | Mirror transform |
| applyTransform(M, x, y) | Apply transform to a point |
Part 2: SVG Toolbox
For: Web developers, designers, anyone working with SVG graphics.
What Can It Do?
SVG files are pictures made of shapes, paths, and effects. This toolbox can:
- Flatten - Bake all transforms into coordinates (no more
transform="rotate(45)") - Convert - Turn circles, rectangles into path commands
- Validate - Find and fix problems in SVG files
- Optimize - Remove unused elements, simplify paths
Why Use This Instead of SVGO?
| | SVGO | svgm (this package) |
|--|------|-----------|
| Math precision | 15 digits (can accumulate errors) | 80 digits (no errors) |
| Verification | None (hope it works) | Mathematical proof each step is correct |
| Attribute handling | May lose clip-path, mask, filter | Guarantees ALL attributes preserved |
| CLI syntax | svgo input.svg -o out.svg | svgm input.svg -o out.svg (identical!) |
| Use case | Quick file size reduction | Precision-critical applications |
Drop-in replacement: The svgm command uses the exact same syntax as SVGO. Just replace svgo with svgm in your scripts.
Use svgm/svg-matrix when: CAD, GIS, scientific visualization, animation, or when visual correctness matters.
Use SVGO when: Quick optimization where small rounding errors are acceptable.
Command Line Tools
svgm - SVGO-Compatible Optimizer (Drop-in Replacement)
A drop-in replacement for SVGO with identical syntax. Simply replace svgo with svgm:
# Basic optimization (same as SVGO)
svgm input.svg -o output.svg
# Optimize folder recursively
svgm -f ./icons/ -o ./optimized/ -r
# Multiple passes for maximum compression
svgm --multipass input.svg -o output.svg
# Pretty print output
svgm --pretty --indent 2 input.svg -o output.svg
# Set precision
svgm -p 2 input.svg -o output.svg
# Show available optimizations
svgm --show-pluginsOptions (SVGO-compatible):
| Option | What It Does |
|--------|--------------|
| -o <file> | Output file or folder |
| -f <folder> | Input folder (batch mode) |
| -r, --recursive | Process folders recursively |
| -p <n> | Decimal precision |
| --multipass | Multiple optimization passes |
| --pretty | Pretty print output |
| --indent <n> | Indentation for pretty print |
| -q, --quiet | Suppress output |
| --datauri <type> | Output as data URI (base64, enc, unenc) |
| --show-plugins | List available optimizations |
Default optimizations (matching SVGO preset-default):
- Remove DOCTYPE, XML processing instructions, comments, metadata
- Remove editor namespaces (Inkscape, Illustrator, etc.)
- Cleanup IDs, attributes, numeric values
- Convert colors to shorter forms
- Collapse groups, merge paths
- Sort attributes for better gzip compression
- And 20+ more optimizations
Run svgm --help for all options.
Embed External Dependencies
Make SVGs self-contained by embedding external resources as data URIs:
# Embed all external dependencies
svgm --embed input.svg -o output.svg
# Embed specific resource types
svgm --embed-images --embed-css --embed-fonts input.svg -o output.svg
# Embed external SVG references with mode selection
svgm --embed-external-svgs --embed-svg-mode extract input.svg -o output.svg
# Embed audio files (for interactive SVGs)
svgm --embed-audio input.svg -o output.svg
# Subset fonts to only include used characters (smaller file size)
svgm --embed-fonts --embed-subset-fonts input.svg -o output.svgEmbed options:
| Option | What It Does |
|--------|--------------|
| --embed | Enable all embedding (images, CSS, fonts, scripts, audio) |
| --embed-images | Embed raster images as data URIs |
| --embed-external-svgs | Embed referenced SVG files |
| --embed-svg-mode <mode> | How to embed SVGs: extract (symbols only) or full |
| --embed-css | Embed external stylesheets |
| --embed-fonts | Embed web fonts as base64 |
| --embed-scripts | Embed external JavaScript |
| --embed-audio | Embed audio files as data URIs |
| --embed-subset-fonts | Subset fonts to used characters only |
| --embed-recursive | Recursively resolve nested dependencies |
| --embed-max-depth <n> | Max recursion depth (default: 10) |
| --embed-timeout <ms> | Fetch timeout in milliseconds (default: 30000) |
| --embed-on-missing <mode> | Action on missing resource: warn, fail, or skip |
Export Embedded Resources
Extract embedded resources from self-contained SVGs back to external files:
# Export all embedded resources to a folder
svgm --export input.svg -o output.svg --export-dir ./resources/
# Dry run - show what would be exported without writing files
svgm --export --export-dry-run input.svg
# Export only images
svgm --export --export-images input.svg -o output.svg --export-dir ./images/
# Extract resources without modifying the SVG
svgm --export --export-only input.svg --export-dir ./resources/
# Custom filename prefix for exported files
svgm --export --export-prefix myapp_ input.svg -o output.svg --export-dir ./assets/Export options:
| Option | What It Does |
|--------|--------------|
| --export | Enable resource extraction mode |
| --export-dir <path> | Output directory for extracted files |
| --export-prefix <str> | Filename prefix for exported files |
| --export-images | Export embedded images only |
| --export-audio | Export embedded audio only |
| --export-video | Export embedded video only |
| --export-scripts | Export inline scripts to .js files |
| --export-styles | Export inline styles to .css files |
| --export-fonts | Export embedded fonts |
| --export-only | Extract files only, don't modify SVG |
| --export-dry-run | Preview extraction without writing files |
| --export-ids <ids> | Only export from specific element IDs |
Inkscape Conversion
Convert Inkscape SVG files to plain/standard SVG by removing editor-specific metadata while preserving SVG 2 features:
# Using svgm
svgm --to-plain-svg inkscape-drawing.svg -o plain.svg
# Keep SVG 1.2 flowText elements (normally removed)
svgm --to-plain-svg --keep-flow-text inkscape-drawing.svg -o plain.svg
# Using svg-matrix CLI
svg-matrix to-plain inkscape-drawing.svg -o plain.svgWhat gets removed:
sodipodi:*elements and attributes (guide lines, named views)inkscape:*elements and attributes (layers, path effects, version)- SVG 1.2 flowText elements (not browser-supported)
- Namespace declarations (xmlns:inkscape, xmlns:sodipodi)
What gets preserved:
- Mesh gradients and hatches (SVG 2 features)
- All standard SVG elements and attributes
- Document structure and styling
Run Without Installing
Use bunx or npx to run the CLI tools without installing the package:
# Using bunx (faster)
bunx @emasoft/svg-matrix svgm input.svg -o output.svg
bunx @emasoft/svg-matrix svg-matrix flatten input.svg -o output.svg
# Using npx
npx @emasoft/svg-matrix svgm input.svg -o output.svg
npx @emasoft/svg-matrix svg-matrix to-plain inkscape.svg -o plain.svgYAML Configuration
Instead of CLI flags, you can use a YAML configuration file:
# svgm.yml
precision: 4
multipass: true
pretty: true
indent: 2
embed:
images: true
externalSVGs: true
externalSVGMode: extract
css: true
fonts: true
scripts: true
audio: true
subsetFonts: true
recursive: true
maxRecursionDepth: 10
timeout: 30000
onMissingResource: warn
export:
outputDir: ./resources/
filenamePrefix: resource_
images: true
audio: true
video: true
scripts: true
styles: true
fonts: true
extractOnly: false
dryRun: false# Use config file
svgm -c svgm.yml input.svg -o output.svgNamespace Preservation
Preserve vendor-specific namespaces during optimization:
# Preserve Inkscape/Sodipodi namespaces (layers, guides, document settings)
svgm --preserve-ns inkscape input.svg -o output.svg
# Preserve multiple vendor namespaces
svgm --preserve-ns inkscape,illustrator input.svg -o output.svg
# Available namespaces: inkscape, sodipodi, illustrator, sketch, ai, serif, vectornator, figmaSVG 2.0 Polyfills
Enable browser compatibility for SVG 2.0 features:
# Add polyfills for mesh gradients and hatches
svgm --svg2-polyfills input.svg -o output.svg
# Combine with namespace preservation
svgm --preserve-ns inkscape --svg2-polyfills input.svg -o output.svgSupported SVG 2.0 features:
- Mesh gradients (
<meshGradient>) - Rendered via canvas fallback - Hatches (
<hatch>) - Converted to SVG 1.1 patterns
svg-matrix - Advanced SVG Processing
For precision-critical operations beyond simple optimization:
# Flatten all transforms into coordinates
svg-matrix flatten input.svg -o output.svg
# Convert shapes (circle, rect, etc.) to paths
svg-matrix convert input.svg -o output.svg
# Normalize all paths to cubic Beziers
svg-matrix normalize input.svg -o output.svg
# Show file information
svg-matrix info input.svgOptions:
| Option | What It Does |
|--------|--------------|
| -o file.svg | Output file |
| -r | Process folders recursively |
| -f | Overwrite existing files |
| -p N | Decimal precision (default: 6, max: 50) |
| -q | Quiet mode |
| -v | Verbose mode |
| --transform-only | Only flatten transforms (faster) |
| --no-clip-paths | Skip clip-path processing |
| --no-masks | Skip mask processing |
Run svg-matrix --help for all options.
svglinter - Find problems in SVG files
svglinter myfile.svg # Check one file
svglinter icons/ # Check all SVGs in folder
svglinter --fix icons/ # Auto-fix problems
svglinter --errors-only icons/ # Only show errorsFinds: broken references, invalid colors, typos in element names, missing attributes.
See full svglinter documentation.
svgfonts - Font Management CLI
Dedicated tool for SVG font operations: embedding, extraction, replacement, and analysis.
# List all fonts used in an SVG
svgfonts list icon.svg
# Embed external fonts as base64 data URIs
svgfonts embed icon.svg -o icon-embedded.svg
# Embed with character subsetting (smaller file size)
svgfonts embed --subset icon.svg -o icon-embedded.svg
# Apply font replacement map
svgfonts replace --map fonts.yml icons/*.svg
# Interactive font management mode
svgfonts interactive document.svg
# Generate YAML replacement map template
svgfonts template > svgm_replacement_map.yml
# Extract embedded fonts to files
svgfonts extract --extract-dir ./fonts/ document.svgCommands:
| Command | Description |
|---------|-------------|
| list | List fonts in SVG (family, type, size, used characters) |
| embed | Embed external fonts as base64 data URIs |
| extract | Extract embedded fonts to files |
| replace | Apply font replacement map from YAML |
| interactive | Interactive font management mode |
| template | Generate YAML replacement map template |
Options:
| Option | Description |
|--------|-------------|
| -o, --output <file> | Output file (default: overwrite input) |
| -r, --recursive | Process directories recursively |
| --subset | Only embed glyphs used in SVG (default) |
| --full | Embed complete font files |
| --map <file> | Path to replacement YAML |
| --extract-dir <dir> | Directory for extracted fonts |
| --no-backup | Skip backup creation |
| --validate | Validate SVG after operations |
| --dry-run | Preview changes without writing |
Font Replacement Map (YAML):
# svgm_replacement_map.yml
replacements:
"Arial": "Inter"
"Times New Roman": "Noto Serif"
"Courier New": "Fira Code"
options:
default_embed: true
default_subset: true
fallback_source: "google"
auto_download: trueUse environment variable SVGM_REPLACEMENT_MAP to set default map path.
Run svgfonts --help for all options.
SVG Toolbox API Quick Reference
GeometryToPath
Convert shapes to path data:
import { GeometryToPath } from '@emasoft/svg-matrix';
const circle = GeometryToPath.circleToPathData(50, 50, 25);
const rect = GeometryToPath.rectToPathData(0, 0, 100, 50, 5, 5);
const ellipse = GeometryToPath.ellipseToPathData(50, 50, 30, 20);SVGFlatten
Parse and transform SVG data:
import { SVGFlatten } from '@emasoft/svg-matrix';
// Parse transform string
const matrix = SVGFlatten.parseTransformAttribute('rotate(45) scale(2)');
// Transform path data
const newPath = SVGFlatten.transformPathData('M 0 0 L 100 100', matrix);
// Resolve CSS units
SVGFlatten.resolveLength('50%', 800); // 400
SVGFlatten.resolveLength('1in', 96); // 96Validation
Find and fix problems:
import { validateSVG, fixInvalidSVG } from '@emasoft/svg-matrix';
const result = await validateSVG('icon.svg');
console.log(result.valid); // true/false
console.log(result.issues); // Array of problems
const fixed = await fixInvalidSVG('broken.svg');
console.log(fixed.svg); // Fixed SVG stringInkscape Support Module
import { InkscapeSupport } from '@emasoft/svg-matrix';
// Check if element is an Inkscape layer
InkscapeSupport.isInkscapeLayer(element);
// Find all layers in document
const layers = InkscapeSupport.findLayers(doc);
// Get document settings from sodipodi:namedview
const settings = InkscapeSupport.getNamedViewSettings(doc);Plain SVG Conversion
import { convertToPlainSVG } from '@emasoft/svg-matrix';
// Convert Inkscape SVG to plain/standard SVG
const plainSVG = await convertToPlainSVG(inkscapeSVG);
// With options
const plainSVG = await convertToPlainSVG(inkscapeSVG, {
removeFlowText: true, // Remove SVG 1.2 flowText (default: true)
removeEmptyDefs: true, // Clean up empty defs (default: true)
removeEmptyGroups: false, // Keep empty groups with IDs (default: false)
});SVG 2.0 Polyfills Module
import { SVG2Polyfills } from '@emasoft/svg-matrix';
// Detect SVG 2.0 features in document
const features = SVG2Polyfills.detectSVG2Features(doc);
// Inject polyfills into document
SVG2Polyfills.injectPolyfills(doc);Embedding and Exporting Resources
import { embedExternalDependencies, exportEmbeddedResources } from '@emasoft/svg-matrix';
// Embed external resources into SVG
const embedded = await embedExternalDependencies(svgString, {
basePath: '/path/to/file.svg',
embedImages: true,
embedFonts: true,
embedCSS: true,
embedScripts: true,
embedAudio: true,
subsetFonts: true,
onMissingResource: 'warn',
timeout: 30000,
});
// Export embedded resources back to external files
const result = await exportEmbeddedResources(embeddedSvg, {
outputDir: './extracted/',
filenamePrefix: 'resource_',
extractImages: true,
extractAudio: true,
extractVideo: true,
extractScripts: true,
extractStyles: true,
extractFonts: true,
extractOnly: false, // true = extract without modifying SVG
dryRun: false, // true = preview only, don't write files
elementIds: null, // filter by element IDs
onProgress: (phase, current, total) => console.log(`${phase}: ${current}/${total}`),
});
console.log(result.extractedFiles); // Array of extracted file info
console.log(result.summary); // { images, audio, scripts, stylesheets, fonts, totalSize }
console.log(result.doc); // Modified SVG string (null if extractOnly)SVG Embedding Options
When using SVG files in web pages, the embedding method affects what features work:
| Embedding Method | Animation (SMIL) | JavaScript | Audio | Use Case |
|------------------|------------------|------------|-------|----------|
| <img src="file.svg"> | Yes | No | No | Static display, icons |
| <object data="file.svg"> | Yes | Yes | No* | Interactive SVGs |
| <embed src="file.svg"> | Yes | Yes | No* | Legacy support |
| <iframe src="file.svg"> | Yes | Yes | No* | Isolated context |
| Inline <svg>...</svg> | Yes | Yes | No* | Full DOM access |
| Standalone (file://) | Yes | Yes | No | Direct file viewing |
| Standalone (http://) | Yes | Yes | No* | Web server |
*Audio requires user interaction due to browser autoplay policies.
SVG Audio Playback Limitations
Modern browsers (Chrome, Firefox, Safari) enforce strict autoplay policies that significantly limit audio playback in SVG files:
| Scenario | Protocol | Example | Audio |
|----------|----------|---------|-------|
| Standalone SVG | file:// | file:///path/to/sample.svg | ❌ Blocked |
| Standalone SVG | http:// | http://localhost:8080/sample.svg | ❌ Blocked |
| HTML with hardcoded audio data URIs | file:// | file:///path/to/sample.html | ❌ Blocked (even after click) |
| HTML with hardcoded audio data URIs | http:// | http://localhost:8080/sample.html | ❌ Blocked (even after click) |
| HTML extracts audio from SVG dynamically | file:// | file:///path/to/sample.html | ✅ Works (after click) |
| HTML extracts audio from SVG dynamically | http:// | http://localhost:8080/sample.html | ✅ Works (after click) |
Key findings:
- Audio elements inside SVG
<foreignObject>are blocked by browsers regardless of protocol or embedding method - Audio sources must be set dynamically (not hardcoded in HTML) for playback to work
- Empty
<audio>elements + dynamicsrcassignment on load +play()on click = success - Hardcoded data URIs in HTML
<source>tags fail even with user click - There is no workaround for truly autonomous SVG audio playback
Technical reasons:
- SVG has no native
<audio>element - audio requires HTML<foreignObject> - Browser autoplay policies block audio without direct user gesture
- Click events inside SVG don't propagate as "trusted" user gestures for audio
- This is a browser security feature, not an SVG limitation
Recommended approach for SVG with audio:
Extract audio sources from the SVG at runtime and play them via HTML <audio> elements. This keeps the SVG file unchanged while enabling audio playback. See samples/SVG_WITH_EMBEDDED_AUDIO/test-embed-with-audio.html for a complete working example.
<!-- HTML wrapper that extracts audio from SVG -->
<div class="player-container">
<object id="svgObject" data="animation.svg" type="image/svg+xml"></object>
<div class="click-overlay" id="overlay">Click to Play</div>
</div>
<audio id="audio_external" preload="auto"></audio>
<script>
const svgObject = document.getElementById('svgObject');
const audio = document.getElementById('audio_external');
let svgRoot = null;
// On SVG load: pause animation and extract audio source
svgObject.addEventListener('load', function() {
const svgDoc = svgObject.contentDocument;
svgRoot = svgDoc.documentElement;
svgRoot.pauseAnimations(); // Pause SMIL animation
// Extract audio source from SVG (audio data stays in SVG file)
const svgAudio = svgDoc.getElementById('audio1');
if (svgAudio) {
const source = svgAudio.querySelector('source');
if (source) audio.src = source.src; // Copy data URI to HTML audio
}
});
// On user click: start animation and audio together
document.getElementById('overlay').addEventListener('click', function() {
this.style.display = 'none';
svgRoot.unpauseAnimations(); // Resume SMIL animation
audio.play(); // Play audio from HTML context
});
</script>This approach:
- Keeps SVG file unchanged (read-only) - audio data URIs remain in SVG
- Extracts audio sources at runtime to HTML
<audio>elements - Pauses SMIL animation until user clicks
- Starts animation and audio simultaneously for perfect sync
- Audio plays from HTML context where browser allows it
Audio Alternatives Research (2024-2025)
Extensive testing confirms there is no way to bypass Chrome's autoplay policy without user interaction. This is by design for user experience.
What Doesn't Work
| Method | Status | Why It Fails |
|--------|--------|--------------|
| SVG 2.0 native <audio> | ❌ | Same autoplay restrictions apply |
| SMIL <audio> element | ❌ | Poor browser support + autoplay blocked |
| <foreignObject> bypass | ❌ | No special privileges for embedded HTML |
| Web Audio API (AudioContext) | ❌ | Starts in "suspended" state, same restrictions |
| CSS audio / background-sound | ❌ | Does not exist in any specification |
| Data URIs / Blob URLs | ❌ | Encoding method doesn't affect autoplay policy |
| SVG onload event | ❌ | Not considered a user gesture |
| SMIL animation beginEvent | ❌ | Animation events are not user gestures |
| xlink:href to audio file | ❌ | Creates link, doesn't trigger playback |
What Works
| Method | Status | Notes | |--------|--------|-------| | User click/touch/keydown | ✅ | Mandatory - no exceptions | | HTML wrapper with dynamic audio extraction | ✅ | Best practice (see example above) | | PWA (Progressive Web App) | ✅ | Desktop only, requires user to install app | | Media Engagement Index (MEI) | ⚠️ | Chrome-only, long-term strategy |
Media Engagement Index (MEI)
Chrome tracks media consumption per domain. After meeting these criteria, autoplay may be allowed on future visits:
- User plays media for >7 seconds
- Audio is unmuted
- Tab is active and visible
- Video element is >200x140 pixels
This is a long-term strategy for apps with repeat users, not an immediate solution.
Browser Compatibility
| Feature | Chrome | Firefox | Safari | Edge | |---------|--------|---------|--------|------| | User gesture required | ✅ | ✅ | ✅ | ✅ | | Web Audio API | ✅ | ✅ | ⚠️ iOS quirks | ✅ | | Data URI audio | ✅ | ⚠️ >1MB issues | ✅ | ✅ | | MEI autoplay | ✅ | ❌ | ❌ | ✅ | | PWA autoplay | ✅ | ⚠️ | ⚠️ | ✅ |
Conclusion: User interaction is mandatory. The HTML wrapper approach with dynamic audio extraction (test-embed-with-audio.html) is the industry best practice recommended by browser vendors.
Exclusive Features (Not in SVGO)
| Function | Description |
|----------|-------------|
| flattenClipPaths() | Flatten clip-paths to geometry |
| flattenMasks() | Flatten masks to geometry |
| flattenGradients() | Bake gradients into fills |
| flattenPatterns() | Expand pattern tiles |
| flattenUseElements() | Inline use/symbol references |
| embedExternalDependencies() | Embed external resources as data URIs |
| exportEmbeddedResources() | Extract embedded resources to files |
| detectCollisions() | GJK collision detection |
| validateSVG() | W3C schema validation |
| decomposeTransform() | Matrix decomposition |
Attribute Preservation
When converting shapes or flattening transforms, ALL attributes are preserved:
| Category | Attributes |
|----------|------------|
| Critical | clip-path, mask, filter, opacity |
| Markers | marker-start, marker-mid, marker-end |
| Paint | fill, stroke, fill-opacity, stroke-opacity |
| Stroke | stroke-width, stroke-dasharray, stroke-linecap |
| URL refs | url(#gradient), url(#pattern), url(#clip) |
Why this matters: Many SVG tools silently drop
clip-pathandmaskattributes, causing visual corruption. svg-matrix preserves everything.
Precision Comparison
svg-matrix vs standard JavaScript (float64):
| Operation | JS Error | svg-matrix Error | Improvement |
|-----------|----------|------------------|-------------|
| Point evaluation | 1.4e-14 | 0 (exact) | 14+ digits |
| Bezier tangent | 1.1e-16 | < 1e-78 | 62+ digits |
| Arc length | 2.8e-13 | < 1e-50 | 37+ digits |
| Bounding box | 1.1e-13 | 0 (exact) | 13+ digits |
| Self-intersection | Boolean only | 1.4e-58 | 58+ digits |
| Bezier-Bezier intersection | 4e-7 | 2.3e-10 | svgpathtools compatible |
Installation
Requires Node.js 24+ or Bun 1.0+
Package Managers
# Bun (recommended - fastest)
bun add @emasoft/svg-matrix
# npm
npm install @emasoft/svg-matrix
# pnpm
pnpm add @emasoft/svg-matrix
# yarn
yarn add @emasoft/svg-matrixIn JavaScript/TypeScript
import { Matrix, Vector, Transforms2D } from '@emasoft/svg-matrix';In HTML (ESM via CDN)
<script type="module">
import { Matrix, Transforms2D } from 'https://esm.sh/@emasoft/svg-matrix';
</script>Browser Bundles (Self-Contained)
Three pre-bundled libraries are available for direct browser use via CDN:
| Bundle | Size | Purpose | Global Variable |
|--------|------|---------|-----------------|
| svg-matrix.min.js | ~45KB | Math only (Matrix, Vector, Transforms) | SVGMatrixLib |
| svg-toolbox.min.js | ~120KB | SVG manipulation (browser-compatible subset) | SVGToolbox |
| svgm.min.js | ~150KB | Complete library (math + toolbox) | SVGM |
Math Library (SVGMatrixLib):
<script src="https://cdn.jsdelivr.net/npm/@emasoft/svg-matrix/dist/svg-matrix.min.js"></script>
<script>
// Note: Uses SVGMatrixLib to avoid conflict with native browser SVGMatrix
const { Matrix, Vector, Transforms2D } = SVGMatrixLib;
// Rotate a point around the origin
const rotation = Transforms2D.rotate(Math.PI / 4);
const [x, y] = Transforms2D.applyTransform(rotation, 10, 0);
console.log('Rotated point:', x.toFixed(4), y.toFixed(4)); // 7.0711 7.0711
</script>SVG Toolbox (SVGToolbox):
<script src="https://cdn.jsdelivr.net/npm/@emasoft/svg-matrix/dist/svg-toolbox.min.js"></script>
<script>
const { GeometryToPath, SVGFlatten, validateSVG } = SVGToolbox;
// Convert circle to path
const circlePath = GeometryToPath.circleToPathData(50, 50, 25);
console.log('Circle as path:', circlePath);
// Parse transform string
const matrix = SVGFlatten.parseTransformAttribute('rotate(45) scale(2)');
</script>Complete Library (SVGM):
<script src="https://cdn.jsdelivr.net/npm/@emasoft/svg-matrix/dist/svgm.min.js"></script>
<script>
// Access both math and toolbox
const { Matrix, Vector, Transforms2D } = SVGM;
const { GeometryToPath, SVGFlatten } = SVGM;
// Use any functionality from either library
const v = Vector.from([3, 4]);
console.log('Vector length:', v.norm().toString()); // 5
</script>Note: Node.js 24+ is required for CLI tools and server-side usage. Browser bundles work in all modern browsers (ES2020+). If you need older Node support, please open an issue.
More Documentation
Third-Party Licenses
This project is licensed under the MIT License (see LICENSE).
SVG 2.0 Polyfill Dependencies
When using the --svg2-polyfills option with svgm or svg-matrix, the following third-party code is embedded in the output SVG:
Inkscape mesh.js Polyfill by Tavmjong Bah
- Purpose: Provides browser compatibility for SVG 2.0 mesh gradients via canvas fallback
- License: GNU General Public License version 3 or later (GPLv3)
- Source: https://gitlab.com/Tavmjong/mesh.js/
- Location:
src/vendor/inkscape-mesh-polyfill.js
Important: When you use --svg2-polyfills, the generated SVG file will contain GPLv3-licensed JavaScript code. This means:
- The output SVG file is subject to GPLv3 terms
- If you distribute the SVG, you must provide source code access as required by GPLv3
- Without
--svg2-polyfills, all generated files remain under MIT license
