← Back to Index

Lesson 1

The SVG Coordinate System

Before you can draw, animate, or transform anything in SVG, you need a rock-solid mental model of how SVG thinks about space. This lesson covers the three foundational concepts: the viewport, the viewBox, and the user coordinate system.

The Core Mental Model

Every SVG establishes its own coordinate system - separate from the HTML document's flow. Understanding this system is the difference between "I tweaked numbers until it looked right" and "I know exactly where things will land."

Core mental model - the camera analogy

Imagine a film crew shooting a scene. There are three distinct things involved:

SVG concept Film analogy What it controls
User coordinate system The stage (infinite, fixed) The abstract space where all shapes are placed. It exists whether anyone is looking at it or not.
viewBox The camera (position + zoom) Selects which rectangle of the stage is captured. Move it to pan; widen/narrow it to zoom out/in.
Viewport (width/height) The screen showing the footage The physical size of the rendered output in the page. Doesn't change what's filmed - only how big the picture appears.

The stage never moves. The shapes never move. When you change the viewBox, you're repositioning or re-zooming the camera - the shapes stay put, but a different portion of the stage ends up on screen. When you change the viewport, you're swapping in a bigger or smaller monitor - same footage, different display size.

User Coordinate System The infinite stage - all shapes live here. It exists whether anything is looking at it or not. viewBox The camera frame - selects which region is captured. scales & renders to Viewport The screen - set by width / height attributes. Controls physical size on the page. width="400"

Left: shapes exist in the user coordinate system (the full stage). The dashed blue rectangle is the viewBox - it frames which part of the stage the camera captures. Right: the viewport - set by width/height - determines how large the result appears on screen.

Viewport vs. viewBox - clearing up the naming confusion

These two sound almost identical, but they answer completely different questions:

Viewport viewBox
Question it answers How big is the SVG on the page? What part of the canvas do we see?
Set by width / height (or CSS) viewBox="min-x min-y w h"
Units Real-world (px, em, %, vw…) Abstract user units (no inherent physical size)
If you change it… Picture gets larger/smaller on screen. Same content, different display size. Different content becomes visible, or same content appears zoomed in/out.
Camera analogy Size of the TV you watch on Where the camera points + its zoom level

The confusing part: they're both "boxes." But one is physical (how many CSS pixels on your page) and the other is logical (which rectangle of the infinite coordinate space gets mapped into that physical box). The viewport never changes the coordinates of your shapes. The viewBox redefines what coordinates are visible and how they scale.

Quick mnemonic: viewport = physical. viewBox = logical (the boundary of what's captured).

1. The Viewport

The viewport is the rectangular region the SVG occupies in the host document. You set it with width and height on the <svg> element:

<svg width="400" height="300">
  <!-- content here -->
</svg>

Key points:

Viewport vs. CSS sizing

You already know from HTML that you can override an element's intrinsic size with CSS. The same applies to SVG - width/height CSS properties override the attributes. This is important: the attribute defines the coordinate system aspect ratio, while CSS defines the actual rendered size.

2. The User Coordinate System (UCS)

Inside every SVG, there's a coordinate system where:

x y 0,0 150, 100

The default SVG coordinate system. Origin top-left, y-axis pointing down.

What the user coordinate system is (and isn't)
  • Cartesian: yes - two perpendicular axes, infinite in all directions. Negative coordinates are perfectly valid.
  • Y-axis: inverted relative to maths. Positive y goes down the page (same convention as HTML/CSS and Canvas 2D - inherited from CRT scan order).
  • Origin: (0, 0) at the top-left of the viewBox by default.
  • Bounded: no. The plane is infinite. You can place shapes at any coordinate - the viewBox simply selects which rectangular portion is visible.

If your SVG is width="400" height="300" with no viewBox, then 1 user unit = 1 CSS pixel, and the coordinate space runs from (0,0) to (400,300).

3. The viewBox

This is where SVG gets powerful. The viewBox attribute redefines the internal coordinate system:

<svg width="400" height="300" viewBox="0 0 800 600">
  <!-- coordinate system now runs 0-800 horizontally, 0-600 vertically -->
  <!-- but it's all rendered into the 400x300 viewport -->
</svg>

The four values are: viewBox="min-x min-y width height"

The camera analogy viewBox is the camera's position and zoom level. A viewBox wider than the viewport zooms out (more content, smaller). A viewBox narrower than the viewport zooms in (less content, bigger). Shifting min-x/min-y pans the camera.

Why viewBox matters

  1. Resolution independence - your coordinates are abstract units, not pixels. The SVG scales to any size.
  2. Aspect ratio control - you define the ratio you want, independent of the container size.
  3. Panning - change min-x/min-y to slide the visible window across a larger canvas (think: maps, scrolling game worlds).
  4. Zoom - change width/height to zoom in/out without altering any child element coordinates.

Interactive: Adjusting the viewBox

viewBox: 0 0 300 200

Drag the sliders to pan and zoom the viewBox. The shapes never move - only the camera does.

Ad

4. preserveAspectRatio

What happens when the viewBox aspect ratio doesn't match the viewport? The preserveAspectRatio attribute controls this:

<svg width="400" height="200" viewBox="0 0 100 100"
     preserveAspectRatio="xMidYMid meet">

The value has two parts:

  1. Alignment - where to place the viewBox within the viewport. Made of two tokens:
    • xMin, xMid, xMax (horizontal)
    • YMin, YMid, YMax (vertical)
    Combined: xMidYMid, xMinYMin, etc.
  2. Meet or Slice:
    • meet - scale uniformly so the entire viewBox is visible (like object-fit: contain). Letterboxing may occur.
    • slice - scale uniformly so the viewport is completely covered (like object-fit: cover). Parts of the viewBox may be clipped.

The special value none stretches non-uniformly to fill the viewport exactly - distorting the content. Rarely what you want.

CSS parallel If you know object-fit and object-position, you already understand this. meet = contain, slice = cover, alignment = object-position. The SVG version predates the CSS version by over a decade.

Visual comparison: meet vs slice vs none

meet (contain) slice (cover) none (stretch) Entire viewBox visible. Pillarboxed. Viewport filled. Top/bottom clipped. Squashed vertically. Shapes distorted.

Same content (a circle, square, and triangle in a tall 100×300 viewBox) rendered into a square 200×200 viewport with different preserveAspectRatio values. The dashed border shows the viewport edge. With meet, the content is pillarboxed. With slice, the viewport is filled but content is cropped. With none, shapes are squashed to fit - the circle becomes an ellipse.

5. Nested Coordinate Systems

Every <svg> element - including nested ones - establishes a new viewport and coordinate system. This is how you can embed independent coordinate spaces within a larger SVG:

<svg width="400" height="300" viewBox="0 0 400 300">
  <!-- outer coordinate system -->
  <rect x="0" y="0" width="400" height="300" fill="#eee"/>

  <!-- nested SVG with its own coordinate system -->
  <svg x="50" y="50" width="200" height="150" viewBox="0 0 100 100">
    <!-- inner coordinates: 0-100 in both axes -->
    <circle cx="50" cy="50" r="40" fill="#306998"/>
  </svg>
</svg>

The <g> element, by contrast, does not create a new coordinate system - it just groups elements and can apply transforms. Lesson 2 covers transforms in depth.

outer: 0,0 outer coords (280,20) inner coords: cx=50, cy=50 Nested <svg> at x=50, y=50 viewBox="0 0 100 100" Inner coords (0-100) map to the 200x150 box

The outer SVG uses a 400×300 coordinate system (grey grid). The nested SVG (dashed blue border) has its own 100×100 coordinate system. The circle at inner (50,50) appears centred in the nested viewport, regardless of where that viewport sits in the outer space.

6. SVG Units and Coordinate Values

SVG supports length units: px, em, rem, ex, pt, pc, cm, mm, in, and %. In practice:

width="200" height="200" (no viewBox) 50x50 = 50px on screen width="200" height="200" viewBox="0 0 400 400" 50x50 = 25px on screen viewBox doubles the coordinate space, halving each unit's pixel size

Left: no viewBox, so 1 user unit = 1 CSS pixel. A 50-unit square is 50px. Right: viewBox="0 0 400 400" mapped into a 200px viewport, so 1 user unit = 0.5 CSS pixels. The same 50-unit square renders at 25px.

7. Document Structure: <title>, <desc>, and <metadata>

Every SVG document can (and should) include descriptive elements that provide accessibility information and machine-readable metadata. These are invisible - they don't render anything - but they're important for screen readers, search engines, and tooling.

<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg"
     role="img" aria-labelledby="svg-title svg-desc">
  <title id="svg-title">Company Logo</title>
  <desc id="svg-desc">A blue circle with the letter K inside,
    representing the company brand.</desc>

  <circle cx="100" cy="50" r="40" fill="#61afef"/>
  <text x="100" y="62" text-anchor="middle"
        font-size="32" fill="white">K</text>
</svg>
Company Logo A blue circle with the letter K inside, representing the company brand. K

Hover over the circle - the <title> shows as a tooltip. Screen readers announce "Company Logo" as the accessible name. The <desc> provides a longer description.

ElementPurposeEquivalent in HTML
<title>Short accessible name. Shown as tooltip on hover. First child of <svg> or any element.alt attribute on <img>
<desc>Longer description for assistive technology. Not displayed visually.aria-describedby target
<metadata>Machine-readable metadata (RDF, Dublin Core, etc.). Ignored by rendering.<meta> tags in <head>
title and desc can go on any element

<title> and <desc> aren't just for the root <svg> - they can be the first child of any SVG element (a circle, a group, a path). This gives individual elements accessible names, which screen readers can announce when the user navigates interactive SVGs.

Putting It Together

The width/height attributes set the viewport size. The viewBox maps a rectangle of user-space coordinates into that viewport. preserveAspectRatio controls how the mapping handles aspect ratio mismatches. Everything you draw uses the user coordinate system defined by the viewBox.

In practice, start every SVG with a viewBox that matches your intended design dimensions. Set width/height for the default display size, but rely on CSS for responsive sizing. This gives you resolution independence from the start, and every coordinate you write is meaningful in your design space rather than tied to a specific pixel count.

Quiz: Check Your Understanding

Question 1

If an SVG has width="200" height="200" viewBox="0 0 400 400", how large is one user unit on screen?

1 CSS pixel
0.5 CSS pixels
2 CSS pixels
Depends on preserveAspectRatio

Question 2

What does changing the viewBox min-x value do?

Moves the SVG element on the page
Shifts all child elements to the right
Pans the camera - shifts which part of the canvas is visible
Changes the origin of the coordinate system permanently

Question 3

Which preserveAspectRatio value is equivalent to CSS object-fit: cover?

xMidYMid meet
xMidYMid slice
none
xMinYMin meet

Hands-On Exercise

Create an SVG that demonstrates these concepts:

  1. Create an SVG with width="300" height="150" and viewBox="0 0 600 300".
  2. Draw a rectangle at x="0" y="0" width="600" height="300" with a fill - it should fill the entire viewport despite being "600 units" wide.
  3. Place a circle at cx="300" cy="150" - it should appear dead centre.
  4. Now change the viewBox to "100 50 400 200" - predict what happens before you save. The camera "zooms in" and "pans" to show only the middle portion of the canvas.