Skip to content

Phaen/cyma

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cyma

Smooth, configurable SVG animations for decorative backgrounds and section dividers.

~3KB gzipped. Zero dependencies. TypeScript-first.

Features

  • Anchor to any edge — fill from the top, bottom, left, or right of the container
  • Fully configurable — speed, colors, wave components, segment resolution, and more
  • Plays well with CSS — uses currentColor by default so it inherits your theme
  • Framework-agnostic — vanilla DOM; works with React, Vue, Svelte, or plain HTML

Install

npm install cyma

Or use a CDN — no build step required:

<script type="module">
  import { wave } from "https://esm.sh/cyma";
  wave(document.getElementById("hero"));
</script>

Quick start

<div id="hero"></div>
import { wave } from "cyma";

const animation = wave(document.getElementById("hero")!);

// animation.stop(), animation.start(), animation.destroy()
// See "Animation controls" below.

Requires a browser with SVG support. For SSR frameworks (Next.js, Nuxt), run Cyma only on the client side.

Shared options

All animations accept these options:

Option Type Default Description
speed number 1 Global speed multiplier
fill string 'currentColor' SVG fill color ('currentColor' inherits from the parent's CSS color)
anchor 'top' | 'bottom' | 'left' | 'right' 'top' Edge the filled region clings to
viewBox { width, height } 1440 × 320 SVG coordinate space
height string CSS height of the SVG element. If omitted, scales with the viewBox aspect ratio.*

* A fixed height that doesn't match the viewBox aspect ratio will stretch the animation non-uniformly. This is most noticeable with bubbles, where round shapes become elliptical.

Animations

wave(container, options?)

A traveling wave built from superimposed sine components. The filled region extends from the anchor edge to the wave contour.

wave(document.getElementById("hero")!, {
  speed: 1.5,
  fill: "#3b82f6",
  anchor: "bottom",
});
Option Type Default Description
segments number 20 Curve resolution (higher = smoother, heavier)
waves WaveComponent[] see below Sinusoidal components to superimpose
taper number 0 Fraction of the width (0–0.5) to smoothly curve down to zero at each edge. 0 = hard borders.

The default waves preset produces a gentle, layered ocean-like motion from four components with varying amplitudes, frequencies, and directions.

WaveComponent

interface WaveComponent {
  amplitude: number; // Peak amplitude in viewBox units
  frequency: number; // Full cycles across the viewport
  phase?: number; // Initial phase offset in radians (default: 0)
  speed: number; // Angular velocity in rad/s (negative = travel left)
}

bubbles(container, options?)

Bumps emerge from the anchor edge, morph into semicircles, then detach and float away as fading circles.

The default viewBox is 1440 × 160 (overriding the shared default).

bubbles(document.getElementById("footer")!, {
  fill: "#8b5cf6",
  anchor: "bottom",
  count: 6,
});
Option Type Default Description
segments number 12 Curve resolution per bump (higher = smoother, heavier)
count number 8 Number of bubbles
size number | { min, max } 35 Bubble radius in viewBox units. A number sets the max (min defaults to 40% of max).
fadeDuration number 6 Maximum fade duration in seconds
fadeDistribution Distribution 'flat' How fade durations are spread across bubbles
distanceDistribution Distribution 'linear' How travel distances are spread across bubbles
taper number 0 Fraction of the width (0–0.5) to taper at each edge. Bump tails curve to zero in the taper zone; bubbles are offset so their semicircles stay outside it.

Distribution is 'flat' | 'linear' | 'normal':

  • flat — all bubbles get the same value
  • linear — uniform spread (equal chance of any value)
  • normal — bell curve (clusters toward the middle, tapers at extremes)

Animation controls

Every animation function returns an AnimationInstance:

interface AnimationInstance {
  readonly svg: SVGSVGElement;
  stop(): void;
  start(resume?: boolean): void;
  destroy(): void;
}
  • svg — Direct reference to the injected SVG element.
  • stop() — Pauses the animation. The SVG remains in the DOM at its last frame.
  • start(resume?) — Resumes the animation. Pass true to continue from the paused position. The default (false) catches up to the global timeline — the animation snaps to the same frame a never-paused instance would be showing.
  • destroy() — Stops the animation and removes the SVG from the DOM.

How it works

Shapes are rendered as cubic Bézier curves via Hermite interpolation, so contours stay smooth at any segment count. All timing derives from Date.now(), meaning separate instances stay in sync regardless of frame rate or tab throttling.

TypeScript

Cyma is written in TypeScript and ships its own type declarations. All option interfaces and the AnimationInstance type are exported:

import type { WaveOptions, BubblesOptions, AnimationInstance } from "cyma";

License

MIT

About

Smooth, configurable SVG animations for decorative backgrounds and section dividers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors