When people first see DrawVerse, the question is always the same: how far does it go? The answer is four levels deep — city, park, shop, brand — but the experience feels boundless. That feeling comes from deliberate design: a coordinate system that stays consistent across zoom levels, a rendering loop tuned for smooth transitions, and an SVG pipeline that keeps lines sharp at every device pixel ratio.
This is the technical story behind how we built it. Not a high-level overview — the actual geometry, the actual render loop, the actual tradeoffs we made.
The coordinate system
Every position in DrawVerse is described by three numbers: (x, y, z). The z value is the zoom level (1 through 4). The x and y values are the viewport center in scene-local units — coordinates relative to the current level's illustration, not the screen.
Scene-local units matter because each level has its own illustration. Level 1 is the full city skyline. Level 2 is the park. They're different SVG files with different internal coordinate spaces. When you zoom from Level 1 into Level 2, you're not zooming a continuous image — you're transitioning between two discrete scenes, and the coordinate origin resets.
// Everything that matters about where the user is
const state = {
x: 0, // viewport center X in scene-local units
y: 0, // viewport center Y in scene-local units
z: 1, // zoom level 1–4
scale: 1.0 // CSS scale factor (animates during transitions)
};This triple is also the URL format: /explore?x=350&y=280&z=3 encodes a specific position at a specific zoom level. Serialize the state → you get a shareable link. Deserialize the URL params on load → you restore exact position. The coordinate system doubles as the persistence layer.
Why SVG, not canvas tiles
Most "infinite zoom" implementations use a tile system: a large raster image sliced into a pyramid of increasingly detailed tiles, fetched on demand as the user zooms. Google Maps works this way. So does Figma's infinite canvas. It's the right approach for photographic images or vector data at massive scale.
DrawVerse chose SVG instead, for two reasons specific to hand-drawn art.
First, hand-drawn lines don't tile well. A tile boundary running through a pencil stroke creates a visible seam. Tiles also require pre-rendering at multiple resolutions, which destroys the texture of hand-drawn line weight. SVG preserves the original stroke data and scales it mathematically — no seams, no texture loss.
Second, our zoom isn't truly continuous. It's four discrete levels, not an infinite-resolution image. The tile pyramid approach is optimized for continuous zoom through many orders of magnitude. For four levels with clean transition boundaries, SVG files per level are simpler and faster to render.
The tradeoff: SVG files are heavier than equivalent JPEG tiles for complex illustrations. We run every scene through SVGO to strip comments, collapse transforms, and remove unused defs before shipping. That typically cuts file size 30–50% with no visible quality loss.
The zoom transition: what actually happens
The visual experience of zooming is a CSS transform: scale() animation — the scene element scales up while the next scene fades in at scale 1. To the viewer it reads as continuous zoom. Under the hood it's a discrete swap at a threshold.
Here's the sequence when a user clicks a hotspot at Level 1 to enter Level 2:
- The hotspot's center coordinates are captured as
(hx, hy)in Level 1 scene units. - A CSS scale animation begins, zooming into the hotspot center (
transform-origin: hx% hy%). - At 80% scale-up, the Level 2 scene element is injected into the DOM at opacity 0.
- At peak scale, Level 1 fades to 0, Level 2 fades to 1, scale resets to 1.0.
- State updates:
z = 2,xandyreset to Level 2's origin, URL updates viahistory.replaceState.
The key implementation detail is step 4: the scale reset is instantaneous (no animation) while the opacity crossfade runs. The user never sees the scale jump because it's hidden by the simultaneous fade. This is the same technique used in film editing — cut on motion to mask the cut.
// Hotspot positions are percentage-based relative to scene viewport
// When clicked, they become the zoom origin for the transition
function zoomToHotspot(hotspot) {
const originX = (hotspot.x / SCENE_WIDTH) * 100;
const originY = (hotspot.y / SCENE_HEIGHT) * 100;
currentScene.style.transformOrigin = `${originX}% ${originY}%`;
currentScene.style.transform = 'scale(12)';
currentScene.style.transition = 'transform 0.6s cubic-bezier(0.4, 0, 0.2, 1)';
setTimeout(() => swapToLevel(hotspot.targetLevel), 500);
}Hotspot positioning: percentage coordinates
Hotspots — the pulsing circles that mark transition points — are positioned using percentage-based coordinates relative to their parent scene's viewport, not pixel values. This matters because scenes render at different sizes depending on the device and window.
A hotspot at { x: 42%, y: 38% } sits at 42% from the left edge and 38% from the top, regardless of whether the scene is rendered at 800px or 1600px. On a 375px mobile viewport the hotspot is still in the right place — tappable, correctly positioned relative to the art.
The underlying illustration is an SVG with a fixed viewBox. The browser scales it to fill the container. Hotspot positions are defined in viewBox units and converted to percentages at render time. This means the hotspot definitions travel with the SVG's coordinate space, not the screen's.
Scroll and pinch: mapping gestures to level transitions
Mouse wheel scroll and touch pinch both map to changes in the scale state variable. Scale increments smoothly — each wheel tick or pinch delta adds to or subtracts from scale with exponential easing. When scale crosses a threshold in either direction, a level transition fires.
The thresholds are deliberately generous: scale > 3.0 triggers zoom-in, scale < 0.35 triggers zoom-out. This prevents accidental level jumps from casual scrolling while still making intentional navigation feel snappy. On mobile, the pinch threshold is tighter — pinch gestures are more deliberate than scroll, so the transition fires at a lower scale factor.
Pan (click-drag on desktop, single-finger swipe on mobile) updates x and y directly, clamped to scene bounds so you can't scroll completely off the illustration. The clamp values are derived from the scene's viewBox dimensions minus the current viewport size — a simple rectangle intersection check.
The brand placement layer
Level 4 — the deepest zoom — is where brand placements live. The technical implementation is straightforward: Level 4 scenes include designated "brand zones," which are rectangular SVG regions with a known coordinate address. A brand placement is an image or SVG asset that fills a zone, served as a static file alongside the scene.
The placement is rendered inline in the scene SVG using a <image> element with an absolute URL. When a placement is active, the zone renders the brand asset. When no placement is booked, the zone renders a hand-drawn placeholder indicating an available spot. Visitors who zoom to Level 4 and find an empty zone are implicitly seeing an ad inventory availability signal.
Brand placements can be deeplinked directly: /explore?x=500&y=400&z=4&utm_source=brand&utm_campaign=your-campaign. The UTM parameters are captured in analytics, so brands see exactly how many visitors zoomed to their placement and where they came from. Every placement is a tracked URL, not just a static image.
Try it yourself
The best way to understand the zoom system is to use it. Open the explorer and zoom through all four levels. When you find a position you want to share, hit the share button in the nav — the URL encodes your exact coordinates. If you want to understand the rendering approach in more depth, the How It Works page covers the architecture with diagrams.