
edge-aura is a dependency-free Canvas 2D library for adding an animated, multicolor glow around your browser viewport.
The glow reacts to taps, keystrokes, and successful saves with bursts of light along the screen edges.
Features
- Smooth color cycling with a bright core and soft inward bloom.
- Seven color palettes and four appearance presets.
- Adjustable corners, glow depth, and dark-background rendering.
- Animated palette transitions and entrance reveals.
- React adapter with idle-frame throttling and reduced-motion support.
How to Use It
Installation
Install the ESM package with npm.
The optional React adapter requires React 18+.
npm install edge-aura
Basic Usage: Vanilla JavaScript
Add a canvas that spans the viewport.
<canvas id="aura-canvas" aria-hidden="true"></canvas>
#aura-canvas {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
pointer-events: none; /* keep the page interactive. */
z-index: 40;
}
Create the engine and drive its animation loop. This example handles viewport resizing and reduced-motion changes.
import { createAuraEngine } from "edge-aura";
const canvas = document.querySelector("#aura-canvas");
const engine = createAuraEngine(canvas);
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
let frameId = 0;
let previousTime = 0;
function draw(now) {
engine.step(previousTime ? now - previousTime : 0);
engine.render();
previousTime = now;
frameId = requestAnimationFrame(draw);
}
function syncMotion() {
cancelAnimationFrame(frameId);
previousTime = 0;
if (reducedMotion.matches) engine.renderStatic();
else frameId = requestAnimationFrame(draw);
}
const onResize = () => {
engine.resize();
if (reducedMotion.matches) engine.renderStatic();
};
window.addEventListener("resize", onResize);
reducedMotion.addEventListener("change", syncMotion);
syncMotion();
function disposeAura() {
cancelAnimationFrame(frameId);
window.removeEventListener("resize", onResize);
reducedMotion.removeEventListener("change", syncMotion);
engine.destroy();
}
Basic Usage: React
The React adapter handles the canvas, animation loop, resizing, and reduced-motion preferences.
import { EdgeAura } from "edge-aura/react";
export default function AppAura() {
return <EdgeAura />;
}
Control the glow from application state and position it above other UI layers with style.zIndex.
<EdgeAura
state={isTyping ? "typing" : "idle"}
savedAt={lastSavedAt}
palette="ocean"
style={{ zIndex: 40 }}
/>
Handle Pointer, Keyboard, and Save Input
Connect pointer and keyboard events to the vanilla engine. tap() takes viewport coordinates; key() takes a horizontal position from 0 to 1. Call pulse() after a successful save.
import { keyCodeToPosition } from "edge-aura";
window.addEventListener("pointerdown", (event) => {
engine.tap({ x: event.clientX, y: event.clientY });
});
window.addEventListener("keydown", (event) => {
const position = keyCodeToPosition(event.code);
if (position) engine.key(position.x);
});
function onSaveSucceeded() {
engine.pulse();
}
For the React adapter, dispatch these window events with the default aura prefix:
import { keyCodeToPosition } from "edge-aura";
window.addEventListener("pointerdown", (event) => {
window.dispatchEvent(new CustomEvent("aura:tap", {
detail: { x: event.clientX, y: event.clientY }
}));
});
window.addEventListener("keydown", (event) => {
const position = keyCodeToPosition(event.code);
if (position) {
window.dispatchEvent(new CustomEvent("aura:key", { detail: position }));
}
});
function onSaveSucceeded() {
window.dispatchEvent(new CustomEvent("aura:saved-pulse"));
}
Change the Palette and Appearance
Use updateOptions() to adjust the active glow. For a dark page, set background: "dark" and choose a blend mode. Call setPalette() after those updates to crossfade to another color scheme.
engine.updateOptions({
geometry: { band: 56, cornerFill: true },
palette: { background: "dark", blendMode: "screen" },
motion: { hueDriftDeg: 6 }
});
engine.setPalette("sunset", { crossfadeMs: 350 });
To start with an appearance preset, use these options in place of the default createAuraEngine(canvas) call shown earlier:
import { createAuraEngine, EDGE_AURA_PRESETS, EDGE_AURA_PALETTES } from "edge-aura";
const engine = createAuraEngine(canvas, {
...EDGE_AURA_PRESETS.subtle,
palette: {
...EDGE_AURA_PRESETS.subtle.palette,
stops: EDGE_AURA_PALETTES.aurora
}
});
Trigger an Entrance Reveal in React
Attach a ref to call kindle(x, y) at the click position. React 18 can use the kindleOrigin prop when mounting the component.
import { useRef } from "react";
import { EdgeAura, type EdgeAuraHandle } from "edge-aura/react";
export default function EntryButton() {
const auraRef = useRef<EdgeAuraHandle>(null);
return (
<>
<button onClick={(event) => auraRef.current?.kindle(event.clientX, event.clientY)}>
Activate glow
</button>
<EdgeAura ref={auraRef} />
</>
);
}
API Reference
Initialization and Exported Helpers
| API | Description |
|---|---|
createAuraEngine(canvas, options?) | Creates an AuraEngine. canvas must be an HTMLCanvasElement with a 2D context; options is an optional EdgeAuraOptions object. |
defineEdgeAuraOptions(options) | Returns the supplied EdgeAuraOptions object for typed configuration. |
EDGE_AURA_DEFAULTS | Built-in geometry, palette, motion, and input defaults. |
NORMALIZE_REF / NORMALIZE_REF_DARK | Default normalization targets for light and dark backgrounds. |
EDGE_AURA_PALETTES | Seven palette stop arrays. |
EDGE_AURA_PRESETS | Four appearance option bundles. |
keyCodeToPosition(code) | Maps a keyboard event.code to a normalized { x, y } position, or null for unmapped keys. |
Public TypeScript Types
| Type | Description |
|---|---|
AuraEngine | Instance returned by createAuraEngine(). |
EdgeAuraOptions | Root options: geometry, palette, motion, input, and seed. |
EdgeAuraGeometryOptions | Geometry option type. |
EdgeAuraPaletteOptions | Palette option type. |
EdgeAuraMotionOptions | Motion option type. |
EdgeAuraInputOptions | Input option type. |
EdgeAuraPaletteStop / EdgeAuraPaletteStops | Single gradient stop tuple and an array of stops. |
EdgeAuraPaletteName / EdgeAuraPresetName | Accepted preset-name types. |
PaletteStops | Deprecated alias of EdgeAuraPaletteStops. |
EdgeAuraProps / AuraState | React component props and its "idle" / "typing" state. |
EdgeAuraHandle | React imperative ref handle with kindle(x, y). |
Geometry Options
| Option (default) | Description |
|---|---|
inset (3) | Distance in pixels between the glow’s centerline and viewport edge. |
cornerRadius (11) | Radius in pixels of the rounded corners. |
cornerFill (false) | Extends the glow into the square viewport corners while retaining the curved inner edge. |
topEdgeFade (0) | Fades the top N pixels of the glow to reduce browser toolbar tinting. |
topCornerFade (0) | Radial fade near the upper corners; radius in pixels. |
band (76) | Depth in pixels of the inward glow band. |
coreSigmaBase (1.6) | Base width of the bright core line in pixels. |
coreSigmaVar (0.6) | Amount of variation in the core width. |
innerSoftBase (1.25) | Base inward-bloom softness multiplier. |
innerSoftVar (0.45) | Variation in the inward-bloom softness multiplier. |
innerSigmaMax (17) | Maximum inward-bloom spread at the default band depth; scales with band. |
Palette Options
Custom
stopsrequire at least two[position, [r, g, b]]entries, starting at0and ending at1. Positions must be finite and nondecreasing; RGB values must be finite.
| Option (default) | Description |
|---|---|
stops (EDGE_AURA_PALETTES.opal) | Gradient stops as [position, [r, g, b]] entries. |
pastel (0.35) | Amount of mixing toward white; 0 retains the original colors. |
coreWhiten (0.2 light; 0.35 dark) | White mixed into the bright core line. |
ringAlpha (0.9) | Maximum ring opacity before normalization. |
normalize (true) | Balances the visual weight of different palettes. |
normalizeTarget (NORMALIZE_REF / NORMALIZE_REF_DARK) | Normalization target for the selected background; accepts an explicit number. |
background ("light") | Background mode for normalization and dark-color processing: "light" or "dark". |
interpolation ("srgb") | Color interpolation: "srgb" or "oklab". |
darkAlphaGamma (0.55) | Brightens the faint glow on dark backgrounds; 1 disables this adjustment. |
darkChroma (1.25) | Saturation multiplier on dark backgrounds; 1 disables the adjustment. |
blendMode ("source-over") | Canvas blending on dark backgrounds: "source-over", "screen", or "plus-lighter". |
Motion Options
| Option (default) | Description |
|---|---|
decay (1.1) | Tap and ambient energy decay rate per second. |
keyDecay (1.9) | Keyboard energy decay rate per second. |
energyCap (1.5) | Maximum accumulated input energy. |
rotateTypingS (3) | Seconds per full color rotation while typing. |
rotateIdleS (8) | Seconds per full color rotation when idle. |
kindleDurS (0.85) | Duration in seconds of the entrance reveal. |
kindleSoftPx (90) | Softness of the reveal wavefront in pixels. |
hueDriftDeg (10) | Hue-drift amplitude in degrees; 0 disables it. |
hueDriftPeriodS (12) | Period of the hue-drift cycle in seconds. |
highlight (off) | Optional moving bloom highlight, configured with arcDeg, periodS, and min. |
highlight.arcDeg (80) | Angular width in degrees of the moving highlight. |
highlight.periodS (6) | Seconds for a full circuit of the highlight. |
highlight.min (0.35) | Bloom intensity outside the highlight, from 0 to 1. |
Input Options
| Option (default) | Description |
|---|---|
keySigma (90) | Horizontal spread in pixels of keyboard hotspots. |
tapSigma (110) | Spread in pixels of pointer hotspots. |
tapEnergy (0.8) | Energy injected per pointer tap. |
keyEnergy (0.9) | Energy injected per keystroke. |
savedPulseEnergy (0.45) | Default energy supplied to pulse(). |
keyXMin (0.08) | Left margin fraction for positioning the keyboard hotspot. |
keyXSpan (0.84) | Fraction of the bottom edge used for keyboard hotspots. |
Top-Level Option
| Option (default) | Description |
|---|---|
seed (random) | Finite numeric seed for deterministic noise phases. Applied when the engine is created. |
Engine Methods
| Method | Description |
|---|---|
step(dtMs) | Advances the animation state by dtMs milliseconds. |
render() | Draws the current animation frame onto the canvas. |
renderStatic() | Draws a dimmed static frame for reduced-motion preferences. |
tap(point) | Adds pointer energy using viewport { x, y } coordinates, or null for a burst without a new target. |
key(x) | Adds a keyboard hotspot at a normalized horizontal position (0 to 1). |
pulse(energy?) | Adds an ambient energy pulse, optionally overriding input.savedPulseEnergy. |
savedPulse() | Deprecated alias for pulse(). |
kindle(x, y) | Reveals the ring from viewport pixel coordinates. |
setTyping(on) | Switches the color-rotation speed between typing and idle states. |
setPalette(palette, opts?) | Applies a preset name or custom stops. Set opts.crossfadeMs for a timed transition; the default is an instant change. |
getNormalization() | Returns { weight, effRingAlpha, effPastel } for diagnostics. |
updateOptions(partial) | Merges partial EdgeAuraOptions into the current configuration; geometry and palette changes are applied immediately. |
resize() | Rechecks viewport dimensions and rebuilds the drawing buffers. |
destroy() | Clears the canvas. Cancel your animation loop and remove host event listeners during teardown. |
React Component Props
| Prop (default) | Description |
|---|---|
state ("idle") | Accepts "idle" or "typing"; changes the ring rotation rate and data-aura-state. |
palette (unset) | A preset name or EdgeAuraPaletteStops. Changes crossfade over 350 ms and override options.palette.stops while set. |
active (true) | Starts or stops animation; false freezes the latest frame. |
quiescentFps (20) | Idle frame-rate limit after roughly five seconds of inactivity. Minimum 5, or false to disable throttling. |
savedAt (0) | Triggers a pulse when it changes to a new nonzero value, except during "typing". |
options ({}) | Reactive engine configuration, merged section by section through updateOptions(). seed is mount-time only. |
eventPrefix ("aura") | Prefix for the window CustomEvents. |
kindleOrigin (null) | Reveals the ring from viewport { x, y } coordinates when the component mounts. |
className (unset) | Adds classes to the wrapper element. |
style (unset) | Overrides wrapper inline defaults; useful for zIndex. |
ref (unset) | React 19 imperative handle exposing kindle(x, y). |
React CustomEvents
The adapter listens for these window CustomEvents. You can change the aura prefix with the eventPrefix prop.
| Event | Detail and effect |
|---|---|
aura:tap | { x, y } viewport coordinates or null; triggers a pointer hotspot. |
aura:key | { x, y } normalized coordinates; uses x for the bottom-edge key position. |
aura:saved-pulse | No detail required; triggers a pulse unless the component is in typing state. |
Palette Presets
| Name | Colors |
|---|---|
opal | Organic blend of warm and cool tones; default palette. |
aurora | Emerald, pale cyan, and sky blue. |
sunset | Warm oranges and magentas. |
ocean | Deep blues and teals. |
sakura | Soft pink and near-white. |
ember | Crimson, maroon, and a bright warm flare. |
ultraviolet | Violet over deep indigo. |
Appearance Presets
| Name | Effect |
|---|---|
subtle | Lower-opacity, softer colors with gentle interaction. |
vivid | More saturated colors, wider glow, and stronger input response. |
calm | Slow color motion and minimal input response. |
thin | Narrow glow band and restrained core variation. |
Markup and Styling Hooks
The React wrapper uses inline position: fixed; inset: 0; pointer-events: none styles. You can override them through the style prop.
| Element or attribute | Purpose |
|---|---|
.edge-aura | Full-viewport wrapper. The className prop appends classes here. |
.edge-aura-canvas | Canvas inside the wrapper. |
data-aura-state | Wrapper attribute with idle or typing. |
aria-hidden | Marks the decorative overlay as hidden from assistive technology. |
Alternatives and Related Resources
- Animated Gradient Border In Pure CSS
- 10+ Stunning Border Styles For UI/UX Design – Bordex.js
- Shaders: 199 WebGPU Components for Interactive Effects







