Panzoom Zoomable Floor Plan — Free JS Snippet

Panzoom Zoomable Floor Plan with Room Focus · Media · Plain HTML, CSS & JS · Live preview

What's included

Features

Wheel, button, drag and pinch zoom/pan from a single small library
Wheel zoom attached to the viewport with passive: false
Room focus using exact scale-and-translate centring maths
Fit-to-view button using the same focus function
Inline SVG plan: sharp at any zoom and individually clickable rooms
Live zoom percentage from the panzoomchange event
Click-versus-drag guard so panning never triggers a room jump
Focusable viewport with an accessible label and real button controls

About this UI Snippet

Panzoom Zoomable Floor Plan — HTML, CSS & JavaScript

Screenshot of the Panzoom Zoomable Floor Plan with Room Focus snippet rendered live

Anything that is bigger than its container — a floor plan, a seating chart, a circuit diagram, a subway map — needs the same interaction: zoom into detail, drag to look around, and a way back to the whole. Panzoom is a small, dependency-free library that does exactly that by applying a CSS transform to a single element, and it handles the parts that are painful to write by hand: pointer and touch events, pinch zoom, momentum-free dragging, and zooming toward the cursor instead of the centre.

Setup is a viewport with overflow hidden and one child element to move. Calling Panzoom(element, options) returns an instance, and there are two integration details worth knowing. First, wheel zoom is not on by default; you attach pz.zoomWithWheel to the wheel event yourself. Attaching it to the fixed viewport rather than the moving element is deliberate, and passive: false is required so the handler can stop the page from scrolling while the user zooms. Second, Panzoom emits a panzoomchange event on the element with the current scale, which the percentage readout uses.

The interesting part is "go to room". Panzoom's transform is scale(s) translate(x, y) around the element's centre, so getting a chosen point to the middle of the viewport is a small piece of algebra rather than a library call. The snippet's focusPoint function solves it: the translation is (viewportCentre − elementCentre) / scale − (point − elementCentre). Choosing the scale by fitting the room's size plus a margin into the viewport, capped at 4x, makes each room fill the view neatly. The same function powers the Fit button, focusing the plan's centre at a scale that fits.

Because the plan is inline SVG, rooms are real elements: they can be clicked, highlighted, given tooltips, and stay sharp at any zoom, which a raster image would not. One subtle bug is handled explicitly — a drag that ends over a room should not count as a click on it — by tracking pointer movement and ignoring clicks that follow a drag of more than four pixels. The viewport is focusable with a descriptive label so keyboard users can find it, and controls are real buttons.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant like Claude to add animated fly-to transitions between rooms, hotspots with tooltips, or a minimap that shows the visible area.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a zoomable floor plan with Panzoom 4 loaded from a CDN.

Requirements:
- Render the plan as inline SVG (six rooms with labels and dimensions) inside a div; call Panzoom(div, { maxScale, minScale, step }) inside an overflow-hidden viewport.
- Attach pz.zoomWithWheel to the viewport's wheel event with { passive: false }, and wire + / − buttons to zoomIn and zoomOut.
- Show the live zoom percentage from the panzoomchange event.
- Implement focusPoint(x, y, scale) using the formula (viewportCentre − elementCentre) / scale − (point − elementCentre), and use it for room chips, clicking rooms, and a Fit button.
- Ignore clicks that follow a drag of more than 4px, and make the viewport keyboard-focusable with an aria-label.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

Requires
<div class="fp-wrap">
  <div class="fp-top">
    <div class="fp-rooms" id="fpRooms" role="group" aria-label="Jump to room"></div>
    <div class="fp-ctl">
      <button type="button" id="fpOut" aria-label="Zoom out">&minus;</button>
      <span id="fpPct" aria-live="polite">100%</span>
      <button type="button" id="fpIn" aria-label="Zoom in">+</button>
      <button type="button" id="fpReset" class="fp-fit">Fit</button>
    </div>
  </div>
  <div class="fp-view" id="fpView" tabindex="0" aria-label="Floor plan. Scroll to zoom, drag to pan.">
    <div class="fp-canvas" id="fpCanvas"></div>
  </div>
  <p class="fp-help" id="fpHelp">Scroll to zoom, drag to pan, or pick a room above.</p>
</div>

Step by step

How to Use

  1. 1
    Scroll to zoomUse the mouse wheel over the plan. It zooms toward the cursor, and the percentage readout updates.
  2. 2
    Drag to panClick and drag to move around the plan; on touch screens, drag with one finger and pinch to zoom.
  3. 3
    Jump to a roomClick a room chip, or click a room on the plan, to zoom and centre on it with its dimensions.
  4. 4
    Use the buttonsThe + and − buttons zoom by a fixed step, and Fit returns to the whole plan.
  5. 5
    Notice the click guardDrag across a room and release; the view does not jump, because a drag is not a click.

Real-world uses

Common Use Cases

Real-estate and venue plans
Let visitors explore layouts and jump to a room. For drag-and-drop editing on a canvas see the Interact.js resizable panels.
Seating charts and maps
Zoom into a section of a theatre or stadium and select a seat.
ADMIN
Technical diagrams
Explore large system, network or circuit diagrams without them shrinking to unreadable size.
Learning transform maths
A worked example of centring a point under a scale-then-translate transform.

Got questions?

Frequently Asked Questions

Capturing the wheel would hijack page scrolling. You opt in by adding pz.zoomWithWheel to a wheel listener, usually on the viewport.

The handler calls preventDefault to stop the page scrolling while zooming, and passive listeners are not allowed to do that.

Panzoom transforms around the element centre, so solve for the translation: (viewportCentre − elementCentre) / scale − (point − elementCentre), then call zoom and pan.

Inline SVG is better: it stays sharp at any zoom and its shapes can be clicked, styled and labelled individually.

Record the pointer-down position and ignore the click if the pointer moved more than a few pixels.

Yes. Panzoom handles multi-touch pinch zoom and one-finger panning out of the box.

Yes. Use the JSX, Vue, Angular or Tailwind export buttons on this page to convert the markup and styles. The behaviour comes from Panzoom, so in a framework project install it with npm install @panzoom/panzoom instead of the CDN tag, create it in useEffect / onMounted / ngAfterViewInit and attach the wheel listener to the viewport, and release it with destroy() when the component unmounts.