Why Fractional Coordinates Cause SVG Antialiasing Blur
Fractional coordinates in an SVG path cause visual antialiasing blur because raster renderers must map continuous vector math onto a discrete grid of physical pixels. When coordinates or line edges land between whole pixels—known as subpixel positions—the rendering engine interpolates the color values across adjacent pixels to simulate smooth rendering. This article explains the mechanics behind subpixel rendering, why centered strokes exacerbate the issue, and how to keep vector graphics visually crisp.
The Discrete Pixel Grid vs. Vector Coordinates
Vector graphics use an infinite, continuous coordinate system defined by mathematical points. However, computer displays are composed of a fixed grid of physical pixels, where each pixel has distinct integer coordinates (e.g., pixel 10, pixel 11).
When an SVG path uses fractional coordinates such as
x="10.5" or y="20.3", the edge of the vector
does not align perfectly with the physical boundary of a pixel.
How Antialiasing Creates the Blur Effect
To represent shapes that do not perfectly cover full pixels, graphics engines (like Skia, Core Graphics, or browser rendering engines) use antialiasing:
- Pixel Coverage Calculation: The renderer calculates what percentage of a pixel’s area is covered by the vector shape.
- Alpha Blending: If a shape only covers 50% of a pixel, the renderer colors that pixel with 50% opacity of the path’s color, blending it with the background.
- Neighboring Bleed: The remaining 50% of the visual weight is assigned to the neighboring pixel.
Instead of a single, sharp 1-pixel line, the renderer produces a 2-pixel wide line with half-intensity color on both sides. The human eye perceives this semi-transparent pixel spread as a soft, blurry edge rather than a sharp line.
The Odd-Pixel Stroke Problem
A common cause of fractional antialiasing blur occurs with strokes on integer coordinates. By default, SVG strokes are centered along the path’s vector line:
- A 1-pixel wide stroke centered on an integer coordinate (e.g.,
x="10") extends0.5pixels to the left and0.5pixels to the right. - This places the stroke’s edges at
9.5and10.5. - Because the stroke covers half of pixel 9 and half of pixel 10, both pixels are rendered with antialiasing, turning a 1-pixel black line into a 2-pixel blurry gray line.
Solutions to Prevent Antialiasing Blur
- Snap to the Pixel Grid: Ensure straight lines and shape boundaries align with whole integer coordinates in your design tool or code.
- Use Half-Pixel Offsets for Odd Strokes: When
drawing a 1-pixel centered stroke, position the coordinate at a
half-pixel (e.g.,
x="10.5"). The stroke will extend0.5pixels in both directions, perfectly filling the space between10.0and11.0. - Use the
shape-renderingAttribute: Applyshape-rendering="crispEdges"in the SVG markup to disable antialiasing on specific paths or the entire canvas, forcing the renderer to snap edges directly to the nearest whole pixel.