@bl0cka.de/blob (0.1.1)

Published 2026-06-11 13:42:50 +02:00 by marcel in npm-registry/blob

Installation

@bl0cka.de:registry=
npm install @bl0cka.de/blob@0.1.1
"@bl0cka.de/blob": "0.1.1"

About this package

@bl0cka.de/blob

Organically morphing decorative blob with gradient or image fill. Zero runtime dependencies; ships compiled ESM with type declarations — no transpilePackages or special bundler setup needed.

npm install @bl0cka.de/blob

Usage

import { createBlob } from '@bl0cka.de/blob'

const blob = createBlob(document.querySelector('#hero-blob')!, {
  fill: {
    type: 'gradient',
    stops: [
      { offset: 0, color: '#8b5cf6' },
      { offset: 1, color: '#22d3ee' },
    ],
    angle: 45,
  },
  animate: 'hover',
})

The blob renders a <canvas> (or <svg>, see renderer) that fills its container (width/height: 100%) — size it via the container. Non-square containers are filled edge-to-edge by default (fit: 'fill'): the shape stretches organically over the rectangle while image fills and gradient angles stay undistorted. Use fit: 'contain' for a centered square blob.

By default the animation runs in a shared Web Worker on an OffscreenCanvas: the morph keeps its frame rate even while the main thread is busy. Where workers or OffscreenCanvas aren't available, it falls back to a main-thread canvas automatically.

Options

Option Default Description
fill violet→cyan 45° gradient { type: 'gradient', stops, shape?: 'linear'|'radial', angle? } or { type: 'image', src } (covers the blob like background-size: cover)
points 8 Anchor points sampled from the noise contour (min 3). 6–10 = blobby, more = busier outline
irregularity 0.35 Radial variation, 0..1
frequency 1 Spatial noise frequency — higher = more lobes
speed 0.15 Morph speed (noise time-units per second)
seed random Same seed → same shape sequence
animate 'always' 'always' | 'hover' | 'scroll' | 'manual'
triggerTarget the container Hover mode: element whose hover animates the blob
scrollDebounceMs 150 Scroll mode: ms after the last scroll event before winding down
attackMs 300 Ramp-up duration for play / trigger start
decayMs 800 Ramp-down duration for pause / trigger end
easing 'ease-in-out' Easing for start/stop ramps: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' or a custom (t: number) => number mapping 0→0, 1→1
transitionMs 400 Crossfade duration for live shape changes
pauseWhenOffscreen true Suspend all work while the blob is outside the viewport
fit 'fill' 'fill' stretches the shape over non-square containers (fills stay undistorted); 'contain' keeps a centered square. Create-time only
renderer 'worker' 'worker' runs animation + drawing in a Web Worker (OffscreenCanvas) — immune to main-thread jank; auto-falls back to 'canvas' where unsupported. 'canvas' renders on the main thread (cheaper than SVG; also accepts non-CORS image fills, which 'worker' cannot fetch). 'svg' is resolution-independent: always crisp when zooming or printing. Create-time only, not switchable via setOptions

Controller

blob.play()                 // ramp the animation up (eased, attackMs)
blob.pause()                // ramp it down (eased, decayMs)
blob.setOptions({  })      // live-update any option
blob.destroy()              // remove SVG, listeners, observer
blob.element                // the HTMLCanvasElement or SVGSVGElement

setOptions never causes a visual jump: shape parameters (points, irregularity, frequency, seed) crossfade over transitionMs while the morph keeps running; everything else applies seamlessly on the next frame. In trigger modes, play()/pause() act as an override until the next trigger event takes back control.

When nothing animates (and during off-screen periods), the shared requestAnimationFrame loop is fully stopped — idle blobs cost nothing. This applies in the worker too.

Worker renderer notes

  • All worker-rendered blobs share one worker; it is terminated when the last one is destroyed.
  • A custom easing function can't cross the worker boundary directly — it is sampled into a 65-entry lookup table and interpolated linearly, which is visually indistinguishable for monotone easings.
  • Image fills are fetched inside the worker, so the URL must be CORS-accessible. Use renderer: 'canvas' for same-origin-restricted images.
  • The worker code is inlined into the bundle and started from a blob URL, so no bundler configuration or asset hosting is needed. Pages with a strict Content-Security-Policy must allow worker-src blob: (otherwise the blob falls back to the main-thread canvas renderer).

Development

npm install
npm run dev          # vite demo at http://localhost:5173, auto-reloads
npm run check        # tsc --noEmit
npm test             # jsdom behavior suite
npm run build        # library build → dist/ (bundle + .d.ts, worker inlined)
npm run build:demo   # demo site build → dist-demo/

Publishing runs check, test and build automatically (prepublishOnly).

Dependencies

Development dependencies

ID Version
jsdom ^29.1.1
release-it ^20.2.0
typescript ^5.8.0
vite ^6.3.0
Details
npm
2026-06-11 13:42:50 +02:00
2
MIT
latest
50 KiB
Assets (1)
Versions (1) View all
0.1.1 2026-06-11