Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

topolines

Animated topographic contour backgrounds. One React component, zero dependencies, drawn on the GPU.

Interactive playground · Try it on CodeSandbox

Install

npm i topolines

React quick start

import { Topolines } from "topolines/react";

export default function Hero() {
  return <Topolines seed="topolines" color="#F2EFE6" style={{ position: "fixed", inset: 0 }} />;
}

Vanilla quick start

import { TopoField } from "topolines";

const field = new TopoField(document.getElementById("host")!, {
  seed: "topolines",
  color: "#F2EFE6",
});

// later
field.pause();
field.destroy();

No build step

Classic script tag (exposes a topolines global):

<script src="https://cdn.jsdelivr.net/npm/topolines"></script>
<script>
  const field = new topolines.TopoField(document.getElementById("topolines-bg"), {
    seed: "hero",
  });
</script>

Or as an ES module, no npm and no bundler needed:

<div id="topolines-bg" style="position:fixed;inset:0"></div>

<script type="module">
  import { TopoField } from "https://esm.sh/topolines";

  const field = new TopoField(document.getElementById("topolines-bg"), {
    seed: "hero",
  });
</script>

Props (TopolinesOptions)

Prop Type Default Description
seed string "topo" Seeds the noise offset. Same seed renders the same field.
speed number 0.012 Animation speed of the field over time.
scale number 1.15 Zoom level of the contour field.
levels number 11 Number of contour bands.
lineWidth number 1.2 Line thickness of each contour.
opacity number 0.16 Overall opacity of the drawn lines.
color string "#C3D82C" Line color. Accepts any CSS color.
drift [number, number] [0.004, 0.002] Constant x/y drift applied to the field per frame.
warp number 0.18 Amount of domain warp applied to the noise.
scrollPan [number, number] [0, 0] Extra pan offset, in the same space as drift, useful for pinned sections.
scrollEase number 0.18 How hard the pan follows the scroll, as a fraction closed per frame at 60Hz (rescaled to the real frame rate). 1 locks it to the scrollbar, so a wheel notch moves the map ~100px in one frame; lower glides between the notches. Ignored when scrollPan is [0, 0].
colorStops { at: number; color: string; opacity: number }[] | undefined undefined Color keyframes interpolated by getProgress(). Overrides color/opacity. Must be sorted by at ascending.
getPanScroll () => number | undefined window.scrollY Overrides the scroll value scrollPan reads, for example to freeze pan across a pinned section.
getProgress () => number | undefined document scroll progress Drives colorStops. Defaults to the page scroll fraction.
maxDpr number 1.5 Caps device pixel ratio used for rendering.
interactive boolean false Enables the cursor bump: contour rings bloom around the pointer.
mouseStrength number 0.35 Strength of the cursor bump when interactive is on.
mouseRadius number 0.35 Radius of the cursor bump when interactive is on.
fallback ReactNode null Rendered instead of the canvas when WebGL is unsupported (React only).
className string | undefined undefined Class applied to the host element (React only).
style CSSProperties | undefined undefined Style applied to the host element (React only).

Notes

  • SSR-safe. No window/document access at module load, safe to import in a Next.js server component tree.
  • Pauses automatically when the canvas is offscreen or the tab is hidden, and resumes when visible again.
  • Respects prefers-reduced-motion: renders one static frame instead of animating.
  • Requires WebGL1 with the OES_standard_derivatives extension. Check ahead of time with isSupported(), or pass a fallback to the React component for automatic handling.
  • If the environment lacks support, the component mounts nothing and reports isSupported() === false / a TopoField instance with .ok === false.
  • Recovers from WebGL context loss. Browsers cap how many contexts a page may keep and evict the oldest, which matters on a page with several fields. The field rebuilds its program and resumes on its own; TopoField.contextLost reports the state in the meantime.
  • Pointer listeners are attached only while interactive is on, and the element rect is cached rather than measured per frame or per pointer event: it is re-read when the element resizes, the page scrolls, the pixel density changes, or half a second has passed. The frame loop itself does no layout reads at all.
  • Every follow in the engine - the cursor bump, its position, the scroll pan - is rescaled to the real frame delta, so the motion is identical at 60, 120 and 144Hz instead of settling more than twice as fast on a high-refresh display.

Docs and interactive playground: https://topolines.idlee.xyz Source: https://github.com/idleCyrex/topolines

License

MIT. Includes a simplex noise implementation from webgl-noise, by Ashima Arts and Stefan Gustavson (MIT). See LICENSE.

About

Animated topographic contour backgrounds for React. One component, zero dependencies, drawn on the GPU.

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages