Zoomer is a lightweight, dependency-free physics engine for ultra-smooth pan, zoom, and rotate interactions in the browser. Designed to bring a polished, iOS-like tactile feel to any web element, it features natural inertia, elastic boundaries (rubber-banding), and precision pivot tracking.
Most pan-and-zoom libraries feel mechanical. Zoomer is built with a custom physics engine to make interactions feel alive and organic. Whether you're building an image gallery, a map viewer, or a complex workspace, Zoomer provides the high-end haptics users expect from modern touch interfaces.
To see Zoomer in action, hop over to: https://alphanull.github.io/Zoomer/
- Direct Manipulation: Naturally pan, zoom, and rotate using multi-touch gestures or mouse, including mousewheel support.
- iOS-style Physics: Built-in inertia, friction, and elastic rubber-band resistance at boundaries.
- Precision Pivot Tracking: Zooming and rotating always happen around the center of the gesture, keeping your focus point stable.
- Mousewheel Precision: Smooth, cursor-centered zooming with automatic pivot adjustment.
- Intelligent Fitting: Built-in
containandcoverstrategies to perfectly align elements within their containers, with optionalautoMinScaleto prevent zooming below the fit scale. - SVG Lossless Zoom Support: Crisp, pixel-perfect SVG rendering at any zoom level. Automatically optimized for inline SVG,
<img>SVG, and CSS background SVG. No pixelation, even at extreme zoom levels. - Smart GPU Acceleration: Selective GPU layer creation -
will-changeis only set during active interaction, saving resources when idle. Perfect for complex SVGs on mobile devices. - High Performance: Optimized with a layout cache and
ResizeObserverto eliminate layout thrashing. Zero CPU usage when idle. - Highly Customizable: Fine-tune every aspect with 20+ options - from physics parameters (friction, stiffness, elasticity) to interaction modes (pan axes, touch behavior) and lifecycle callbacks.
- Zero Dependencies: Pure Vanilla JS, lightweight, and ready to drop into any project.
npm install @alphanull/zoomerDownload latest version from jsDelivr Download latest version from unpkg
Zoomer can be used as ES6 module (recommended) but also via require in NodeJS or with direct access to a global variable:
import Zoomer from '@alphanull/zoomer';const Zoomer = require('@alphanull/zoomer');<script src="path/to/Zoomer.min.cjs"></script>const Zoomer = window.Zoomer;const element = document.querySelector('.my-element');
const container = document.querySelector('.container'); // container defines boundaries
const zoomer = new Zoomer(element, {
container: container,
fitContainer: 'contain',
boundsEnabled: true,
minScale: 1,
maxScale: 4
});
zoomer.applyTransform({ x: 0, y: 0, scale: 2, rotation: 45 });| Option | Default | Description |
|---|---|---|
initialX |
0 |
Initial horizontal translation. |
initialY |
0 |
Initial vertical translation. |
initialScale |
1 |
Initial scale factor. |
initialRotation |
0 |
Initial rotation in degrees. |
minScale |
0.5 |
Minimum allowed scale factor. |
maxScale |
2 |
Maximum allowed scale factor. |
friction |
0.1 |
Friction factor (0 to 1). Higher values stop faster. |
springStiffness |
0.2 |
Strength of the bounce-back effect. |
resistance |
0.8 |
Resistance at boundaries (0 to 1). 1 = hard limit. |
maxElasticity |
100 |
Max pixels the element can stretch beyond limits. |
physicsEnabled |
true |
Enable/disable inertia and spring-back. |
panEnabled |
'on' |
Panning mode: 'on', 'off', 'xAxis', or 'yAxis'. |
panMultiTouchOnly |
false |
If true, panning with single touch is disabled (allows normal scrolling on touch devices). |
zoomEnabled |
true |
Enable/disable zooming (touch & wheel). |
rotationEnabled |
true |
Enable/disable rotation (touch). |
keyboardEnabled |
true |
Enable/disable keyboard controls (arrows, +/-). |
doubleTapEnabled |
true |
Enable/disable double tap to toggle zoom. |
wheelModifier |
'none' |
Modifier key for wheel zoom: 'none', 'alt', 'control', or 'meta'. |
wheelRotationModifier |
'alt' |
Modifier key for wheel rotation: 'none', 'alt', 'control', 'meta', or 'shift'. |
container |
null |
DOM element used for boundaries and initial fitting. |
boundsEnabled |
false |
If true, the element will be contained within the container. |
fitContainer |
'none' |
Initial scaling strategy: 'none', 'cover', or 'contain'. |
autoMinScale |
false |
If true, prevents zooming below fitScale when fitContainer is set. |
onRender |
null |
Custom render function: (state, element) => void. |
onUpdate |
null |
Callback for state updates: (state) => void. |
onInteractionStart |
null |
Triggered when user starts interaction. |
onInteractionEnd |
null |
Triggered when user stops interaction. |
| Method | Arguments | Description |
|---|---|---|
reset |
immediate: boolean |
Resets the element to its initial state. |
applyTransform |
transform: object, immediate: boolean |
Programmatically sets X, Y, Scale, and/or Rotation. |
updateLayout |
applyFit: boolean |
Re-calculates layout. Set true to re-apply container fitting. |
destroy |
- | Removes all listeners and stops the update loop. |
immediate(boolean): Iftrue, the element jumps to the new state instantly. Iffalse(default), the transition is handled by the physics engine for a smooth "homing" effect.transform(object): A partial state object. You only need to provide the properties you want to change:x(number): Target horizontal translation.y(number): Target vertical translation.scale(number): Target scale factor.rotation(number): Target rotation in degrees.
The state object (accessible via zoomer.state or in callbacks) contains the current transformation and velocity:
| Property | Type | Description |
|---|---|---|
x |
number |
Current horizontal translation in pixels. |
y |
number |
Current vertical translation in pixels. |
scale |
number |
Current scale factor. |
rotation |
number |
Current rotation in degrees. |
pivotX |
number |
Last zoom/rotation pivot X (local coordinates). |
pivotY |
number |
Last zoom/rotation pivot Y (local coordinates). |
vx |
number |
Current velocity on the X axis. |
vy |
number |
Current velocity on the Y axis. |
vs |
number |
Current scale velocity. |
vr |
number |
Current rotation velocity in degrees/frame. |
isInteracting |
boolean |
Whether a user interaction is currently in progress. |
# Install dependencies
npm install
# Build the minified library
npm run build
# Run tests
npm run test
# Generate docs
npm run doc
# Run the demo
npm run demoFor more detailed docs, see JSDoc Documentation
Zoomer supports all major browsers on all platforms (i.e. macOS, iOS, Windows, Android & Linux) released since ~2019-2020 and later, including Chrome (v79+), Firefox (v75+) and Safari (v13+).
Zoomer includes optimizations for crisp SVG rendering at high zoom levels:
- Chrome/Safari: Perfect sharp rendering for all SVG types (inline SVG,
<img>SVG, CSS background SVG) - Firefox:
- ✅ Inline SVG: Sharp rendering (best quality)
⚠️ <img>SVG: Improved but may show blur at high zoom⚠️ CSS background SVG: Improved but may show stronger blur at high zoom
For the best rendering quality across all browsers, inline SVG is recommended. The library automatically detects SVG elements and applies appropriate rendering optimizations.
Copyright © 2025-present Frank Kudermann @ alphanull.de