pixi-viewport
A highly configurable viewport/2D camera designed to work with pixi.js.
Features include dragging, pinch-to-zoom, mouse wheel zooming, decelerated dragging, follow target, animate, snap to point, snap to zoom, clamping, bouncing on edges, and move on mouse edges. See the live examples below to try out all of these features.
All features are configurable and removable, so set up the viewport to be exactly what you need.
If pixi-viewport is useful to you, you can sponsor its development on GitHub.
AI-assisted development
Starting in October 2026, Claude (an AI model by Anthropic) assists with editing this code, including code review, bug fixes, and changes to documentation. A maintainer reviews every change before it is released. If you prefer not to use software developed with AI assistance, please take this into account when choosing whether to use pixi-viewport.
v7.0.1
Fixes loading in Node (server-side rendering, Vitest and other test runners). package.json now has an exports map, so import { Viewport } from 'pixi-viewport' loads the ES module build and shares your copy of pixi.js. Before, Node loaded the UMD build, so that import threw Named export 'Viewport' not found. Deep imports such as pixi-viewport/dist/types keep working. Bundlers aren't affected.
v7.0.0
Tested with pixi.js v8.22. This release fixes many long-standing bugs. Some of those fixes change behavior that apps may rely on, so this is a major version.
Breaking changes:
- Built-in plugins always run in their default order (drag, pinch, wheel, follow, mouse-edges, edge-scroll, decelerate, animate, bounce, snap-zoom, clamp-zoom, snap, clamp), whatever order you add them in. Previously, each call moved that plugin to the end of a list shared by every viewport, so
viewport.clamp().drag()ran drag after clamp. User plugins added withviewport.plugins.add()still go at the end unless you pass an index. clamp()with no options no longer clamps.falseforleft,right,top, orbottomnow means "don't clamp this side" (it was treated as 0).drag-startanddrag-endreport the pointer's screen position inscreen.
New:
viewport.edgeScroll()scrolls when the mouse is near the screen edges, withspeedin screen pixels per second. It replacesmouseEdges(), which is deprecated but unchanged.drag({ disableOneFingerDrag: true })ignores one-finger touch drags so the page can scroll.clampZoom({ keepAspectRatio: false })clamps one axis at a time, for zooming along a single axis.wheel({ trackpadPinch: true, trackpadPinchSpeed: 2.5 })speeds up trackpad pinch zooming without changing mouse wheel zooming. The default (1) keeps the current speed, which is slow for a pinch.new Viewport({ ignoreWheelFromOtherElements: true })ignores wheel events that bubble up from other elements, such as HTML overlays.- A pixi object inside the viewport can call
stopPropagation()on itswheelevent to keep the viewport from zooming. WithpassiveWheel: false, the page doesn't scroll either. getVisibleBounds()takes an optionalRectangleto write into, and the default hit area no longer allocates a new one every frame.- The event types (
DragEvent,MovedEvent,ZoomedEvent, and others) are exported. - A new demo site: a playground with presets and a live code panel, plus examples for embedding a viewport in a scrolling page and for overlays. The API docs are generated with TypeDoc.
Fixed:
- Clamp now stops deceleration at the edge (no more jitter), and re-clamps when only the screen size changes.
follow()works with targets nested in other containers,accelerationno longer exceedsspeed, and following a target that gets destroyed stops instead of throwing every frame.- Animate's
removeOnInterruptalso stops on wheel events. - Negative scale (e.g., a y-axis pointing up) no longer breaks input.
mouse-edge-startandmouse-edge-endevents are emitted, includingmouse-edge-endwhen the plugin is paused or removed while scrolling.snapZoom({ time: 0 })no longer throws.- Touches whose
pointerupnever arrived (e.g., iOS suspending the page) are cleared when a new gesture starts. - Type declarations use
.jsimport extensions (works withmoduleResolution: NodeNext) and include JSDoc comments. - The UMD build uses the
PIXIglobal. - Decelerate and Wheel remove their event listeners when removed.
- With
wheel({ wheelZoom: false }), a plain wheel event the viewport doesn't use no longer stops the page from scrolling (it was blocked wheneverpassiveWheelwasfalse).
Documented without changing behavior: the wheel percent option and the mouseEdges speed option (see their JSDoc).
v6.0.0
Moves pixi-viewport to pixi.js v8+ - sorry for the confusion.
v5.0.2
Moves pixi-viewport to pixi.js v7.2+.
NOTE: there is a breaking change since in pixi.js v7.2 interactive (boolean) was deprecated in favor of eventMode.
v5
Moves pixi-viewport to pixi.js v7 (thanks @cuire!).
NOTE: there is a breaking change since pixi-viewport moved to pixi's new event system. options.interaction is removed and you need pass options.events to the viewport for it to work properly. The events object can be found at pixi's renderer.events or app.renderer.events.
const viewport = new Viewport({ events: renderer.events });
// or
// const viewport = new Viewport({ events: app.renderer.events });
v4.30.0+
This project was migrated to Typescript (thanks @ShukantPal!). All functionality should be the same. The live Example has been updated.
Live Examples
- Playground: try every plugin, switch between presets (map, edge scrolling, follow, timeline, bounce), and copy the code for your setup.
- In a scrolling page: embed a viewport in an article without trapping the mouse wheel or one-finger swipes.
- Overlays: keep HTML panels and scrollable pixi objects from zooming the viewport underneath.
API Documentation
https://viewport.pixijs.io/api/
Simple Example
import * as PIXI from "pixi.js";
import { Viewport } from "pixi-viewport";
// or with require
// const PIXI = require('pixi.js')
// const Viewport = require('pixi-viewport').Viewport
const app = new PIXI.Application();
await app.init({ resizeTo: window });
document.body.appendChild(app.canvas);
// create viewport
const viewport = new Viewport({
screenWidth: window.innerWidth,
screenHeight: window.innerHeight,
worldWidth: 1000,
worldHeight: 1000,
events: app.renderer.events, // pixi's EventSystem is required for input to work
});
// add the viewport to the stage
app.stage.addChild(viewport);
// activate plugins
viewport.drag().pinch().wheel().decelerate();
// add a red box
const sprite = viewport.addChild(new PIXI.Sprite(PIXI.Texture.WHITE));
sprite.tint = 0xff0000;
sprite.width = sprite.height = 100;
sprite.position.set(100, 100);
Installation
npm i pixi-viewport
or
pnpm add pixi-viewport
or
yarn add pixi-viewport
or grab the latest release and use it:
<script src="/directory-to-file/pixi.min.js"></script>
<script src="/directory-to-file/pixi_viewport.umd.cjs"></script>
<script>
const viewport = new pixi_viewport.Viewport(options);
</script>
The UMD build expects pixi.js to be loaded first as the global PIXI. For ES modules, use pixi_viewport.js with an import map that resolves pixi.js.
Migration from pixi-viewport v3 to v4
Viewport needs to be imported or required as follows:
import { Viewport } from "pixi-viewport";
// or
const Viewport = require("pixi-viewport").Viewport;
Plugins have been moved to their own object:
// viewport.pausePlugin('drag')
viewport.plugins.pause("drag");
// viewport.resumePlugin('drag')
viewport.plugins.resume("drag");
// viewport.removePlugin('drag')
viewport.plugins.remove("drag");
// viewport.userPlugin('name', plugin, index)
viewport.plugins.add("name", plugin, index);
Tests
Tests use Vitest with jsdom.
- Clone repository
- pnpm install
- pnpm test (or
pnpm test:watchto rerun on changes) - pnpm coverage (for a coverage report)
Development Recipe
This project uses pnpm (the version is pinned in package.json's packageManager field).
- clone repository
- pnpm install
- pnpm dev
- open browser to http://localhost:5173 (the playground; the example pages are linked from its header)
- pnpm run docs (builds the demo site and API docs into docs/dist;
pnpm docs:serveto preview)
PRs are more than welcome!
Other Libraries
If you liked pixi-viewport, please try my other open source libraries:
- pixi-scrollbox - pixi.js scrollbox: a masked box that can scroll vertically or horizontally with scrollbars (uses pixi-viewport)
- pixi-ease - pixi.js animation library using easing functions
- intersects - a simple collection of 2d collision/intersects functions. Supports points, circles, lines, axis-aligned boxes, and polygons
license
MIT License (c) 2024 YOPEY YOPEY LLC by David Figatner (david@yopeyopey.com)