Changelog

Version history notes of the Core and the React packages

Core

0.18.0 - 28.04.2026

SVG renderer trail pool is now sized to trailLength at the time of construction instead of a hardcoded cap, which was 200. Before the change, trailLength values above 200 would silently clip in the SVG renderer while the canvas equivalent would have no problem rendering it fully.

Engine.trailLength is now exposed on Engine objects. It reflects the maximum trail capacity set at the time of engine creation.

0.17.3 - 24.04.2026

cyclePos in shared renderer where palette color is retrieved now uses the safe double-modulo form

0.17.0 - 24.04.2026

Four new built-in curves: rose52, star, star4, star7

0.16.0 - 23.04.2026

destroy() now rejects any pending morphTo Promise.

Previously, if a Sarmal instance was destroyed druing a morph, the Promise was never settled.

The rejection is swallowed by the .catch(() => {}) in @sarmal/react’s useSarmal hook. Callers who await morphTo directly should handle the rejection if they call destroy() before the morph completes.

0.15.1 - 19.04.2026

Replaced getBoundingClientRect() approach of getting container size with offsetWidth and offsetHeight.

The getBoundingClientRect() would cause unexpected width/height output, because it would return visually scaled size affected by CSS. So, if one were to apply transform: scale(0.99) to the canvas, but 280px for the actual canvas size, the end result would be 277.2px

The offset values now give the inherent layout box size, unaffected by CSS transforms

0.15.0 - 19.04.2026

Breaking
The palette option is removed. Colors are now unified under trailColor which accepts a single hex string OR an array of hex strings.
// Before
const sarmal = createSarmal(canvas, curves.astroid, {
  trailStyle: "gradient-animated",
  palette: "bard",
});

// After
import { palettes } from "@sarmal/core";
const sarmal = createSarmal(canvas, curves.rose3, {
  trailStyle: "gradient-animated",
  trailColor: palettes.sunset,
});

setRenderOptions(partial) is added to SarmalInstance. Change colors and trail style on a live instance without destroying and recreating it.

sarmal.setRenderOptions({ trailColor: palettes.sunset });

sarmal.setRenderOptions({ trailStyle: "default", trailColor: "#c0143c" });

sarmal.setRenderOptions({ headColor: "#ffffff" }); // override
sarmal.setRenderOptions({ headColor: null });      // inherited from trail color

Validation rules: The current setup requeires that all colors be 6-digit hex strings. skeletonColor also accepts "transparent". If any field fails validation, then no fields are mutated.


0.14.0 - 18.04.2026

Animated gradients are now supported in SVG renderer

trailStyle and palette options now work in createSarmalSVG with identical behavior to createSarmal. Previously these options were only available in canvas.

const sarmal = createSarmalSVG(container, curves.astroid, {
  trailStyle: 'gradient-animated',
  palette: 'bard',
});

0.13.0 - 16.04.2026

Breaking
SarmalInstance methods start() and stop() are renamed to play() and pause() to match similar libraries
  • start() play()
  • stop() pause()

autoStart added: createSarmal and createSarmalSVG now default to autoStart: true, so the animation begins immediately on creation. If you were calling .start() right after creation, you can remove that call. If you want to defer playback, pass autoStart: false.

// Before
const sarmal = createSarmal(canvas, curves.artemis2)
sarmal.start()

// After
const sarmal = createSarmal(canvas, curves.artemis2)
// No call needed here

Deferred start:

const sarmal = createSarmal(canvas, curves.artemis2, { autoStart: false })
// later...
sarmal.play()

A new initialT option lets you seek to a specific position before the first frame, replacing the common pattern of play() then seek(t).

// Start at a specific position
const sarmal = createSarmal(canvas, curves.artemis2, { initialT: Math.PI })

React

0.1.0 - 23.04.2026

@sarmal/react is a new package that provides a React wrapper over @sarmal/core. It exports a <Sarmal> component for drop-in usage and a useSarmal hook for imperative control.

Tip
Curve changes use morphTo under the hood
import { Sarmal } from "@sarmal/react";
import { curves } from "@sarmal/core";

<Sarmal curve={curves.rose3} />

Peer dependencies: react >= 18 and @sarmal/core. Each source file is marked with "use client" for SSR compatibility.