npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@phaen/cyma

v0.1.1

Published

Lightweight SVG animation library for decorative backgrounds and section dividers

Readme

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