Why Is My SVG Blurry? Fixing Common SVG Rendering Issues
SVGs are supposed to be infinitely scalable. So why does yours look fuzzy, blurry, or just... off?
Blurry SVGs usually aren't actually blurry—something in how they're rendered is causing visual artifacts. This guide covers every common cause and how to fix each one.
Quick Diagnosis
Before diving into fixes, identify your symptom:
| Symptom | Likely Cause | Jump to | |---------|--------------|---------| | Fuzzy edges on lines | Sub-pixel positioning | Fix 1 | | Blurry on retina/high-DPI | Rasterization before scaling | Fix 2 | | Blurry after CSS transform | Transform origin issues | Fix 3 | | Looks fine zoomed, blurry at 100% | Anti-aliasing artifacts | Fix 4 | | Only blurry in one browser | Browser rendering differences | Fix 5 | | Blurry when animated | Animation rendering | Fix 6 |
Fix 1: Sub-Pixel Positioning
The problem: Lines or shapes positioned at fractional coordinates render blurry because the browser anti-aliases them across multiple pixels.
Why It Happens
A 1-pixel line at x="10.5" straddles two pixels. The browser splits the color between them, creating a fuzzy 2-pixel line:
Without sub-pixel: With sub-pixel:
█ ░█░
(sharp 1px line) (blurry 2px line)
How to Fix
Option 1: Round coordinates to whole numbers
<!-- Blurry -->
<line x1="10.5" y1="0" x2="10.5" y2="100" stroke="#000" stroke-width="1"/>
<!-- Sharp -->
<line x1="10" y1="0" x2="10" y2="100" stroke="#000" stroke-width="1"/>
Option 2: Use half-pixel offset for 1px lines
For a perfectly sharp 1px line, position it at x.5:
<!-- Sharp 1px line in the center of a pixel -->
<line x1="10.5" y1="0.5" x2="10.5" y2="99.5" stroke="#000" stroke-width="1"/>
Wait, that contradicts option 1? Here's the nuance:
- For odd-width strokes (1px, 3px), use .5 offsets
- For even-width strokes (2px, 4px), use whole numbers
<!-- 1px stroke: use .5 offset -->
<line x1="10.5" y1="0" x2="10.5" y2="100" stroke-width="1"/>
<!-- 2px stroke: use whole numbers -->
<line x1="10" y1="0" x2="10" y2="100" stroke-width="2"/>
Option 3: Use shape-rendering
<svg shape-rendering="crispEdges">
<!-- All shapes render with sharp edges, no anti-aliasing -->
</svg>
Values:
auto— Browser decides (default)crispEdges— Sharp edges, may look jagged on diagonalsgeometricPrecision— Prioritize shape accuracyoptimizeSpeed— Fastest rendering
Best for: Icons, UI elements, anything with straight lines.
In Practice
<!-- Before: blurry grid -->
<svg viewBox="0 0 100 100">
<line x1="25" y1="0" x2="25" y2="100" stroke="#ccc" stroke-width="1"/>
<line x1="50" y1="0" x2="50" y2="100" stroke="#ccc" stroke-width="1"/>
<line x1="75" y1="0" x2="75" y2="100" stroke="#ccc" stroke-width="1"/>
</svg>
<!-- After: sharp grid -->
<svg viewBox="0 0 100 100" shape-rendering="crispEdges">
<line x1="25" y1="0" x2="25" y2="100" stroke="#ccc" stroke-width="1"/>
<line x1="50" y1="0" x2="50" y2="100" stroke="#ccc" stroke-width="1"/>
<line x1="75" y1="0" x2="75" y2="100" stroke="#ccc" stroke-width="1"/>
</svg>
Fix 2: Retina Display Issues
The problem: SVG looks crisp on a standard display but blurry on retina/high-DPI screens.
Why It Happens
This usually means the SVG is being rasterized (converted to pixels) before being scaled up. Common causes:
- CSS filters on the SVG (blur, drop-shadow)
- SVG inside a canvas element
- Background-image SVG with incorrect sizing
- CSS transform with will-change or translateZ
How to Fix
Check for rasterization triggers:
/* These can cause rasterization */
.svg-container {
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); /* Triggers rasterization */
transform: translateZ(0); /* Creates new layer, may rasterize */
will-change: transform; /* Prepares for animation, may rasterize */
}
Option 1: Remove or move filters
/* Instead of filter on SVG */
.svg-container {
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
}
/* Apply shadow to a wrapper */
.svg-wrapper {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
Option 2: Force vector rendering
svg {
transform: none;
will-change: auto;
filter: none;
}
Option 3: For background SVGs, use proper sizing
/* Bad: may rasterize at 1x then scale */
.element {
background-image: url('icon.svg');
background-size: 100px 100px;
}
/* Better: use inline SVG or ensure SVG has correct viewBox */
Option 4: Double-check image-rendering
svg {
image-rendering: auto; /* or -webkit-optimize-contrast */
}
Test for Retina Issues
- Open DevTools
- Toggle device emulation
- Set DPR (device pixel ratio) to 2 or 3
- If SVG looks blurry only at high DPR, you have a rasterization issue
Fix 3: CSS Transform Problems
The problem: SVG looks sharp until you apply a CSS transform, then it gets blurry.
Why It Happens
Transforms can trigger hardware acceleration, which rasterizes the element. The rasterized bitmap then gets scaled, causing blur.
/* This can cause blur */
svg {
transform: scale(1.5);
}
How to Fix
Option 1: Scale the viewBox instead
Instead of CSS transform:
<!-- Original -->
<svg viewBox="0 0 100 100" width="100" height="100">
<!-- Scaled up (change width/height, keep viewBox) -->
<svg viewBox="0 0 100 100" width="150" height="150">
Option 2: Use scale with transform-origin
svg {
transform: scale(1.5);
transform-origin: center center;
/* Some browsers render sharper with explicit origin */
}
Option 3: Force repaint after transform
svg {
transform: scale(1.5) translateZ(0);
backface-visibility: hidden;
}
Sometimes forcing a 3D context actually helps. Test both with and without.
Option 4: Scale container, not SVG
<div class="scaled-container">
<svg viewBox="0 0 100 100">...</svg>
</div>
.scaled-container {
width: 150px;
height: 150px;
}
.scaled-container svg {
width: 100%;
height: 100%;
}
Fix 4: Anti-Aliasing Artifacts
The problem: Edges look fuzzy or have color fringes, especially on diagonal lines.
Why It Happens
Anti-aliasing smooths edges by blending colors at boundaries. Sometimes this creates unwanted effects:
- Gray fringes on black shapes over white backgrounds
- Color bleeding between adjacent shapes
- Soft edges where you want sharp ones
How to Fix
Option 1: shape-rendering: crispEdges
<svg shape-rendering="crispEdges">
<!-- Sharp edges, no anti-aliasing -->
</svg>
Tradeoff: Diagonal lines will look jagged (stair-stepped).
Option 2: Adjust stroke alignment
SVG strokes are centered by default. For shapes on pixel boundaries:
/* Strokes render outside the shape path */
path {
stroke-alignment: outer; /* Not widely supported yet */
}
Workaround: manually offset paths to account for stroke width.
Option 3: Add a subtle background
If you're seeing white fringes on dark shapes:
<svg style="background: #fafafa;">
<!-- Dark shapes on very-light-gray hides white fringes -->
</svg>
Option 4: Overlap shapes slightly
For adjacent shapes with color bleeding:
<!-- Gap between shapes can show through -->
<rect x="0" y="0" width="50" height="100" fill="red"/>
<rect x="50" y="0" width="50" height="100" fill="blue"/>
<!-- Overlap by 1px -->
<rect x="0" y="0" width="51" height="100" fill="red"/>
<rect x="50" y="0" width="50" height="100" fill="blue"/>
Fix 5: Browser Rendering Differences
The problem: SVG looks perfect in Chrome, blurry in Firefox (or vice versa).
Why It Happens
Browsers use different rendering engines with different anti-aliasing algorithms:
- Chrome/Edge: Skia
- Firefox: WebRender (Rust-based)
- Safari: Core Graphics
Each makes different tradeoffs between speed, accuracy, and appearance.
How to Fix
Option 1: Test and adjust shape-rendering per browser
/* Base style */
svg {
shape-rendering: geometricPrecision;
}
/* Firefox-specific */
@-moz-document url-prefix() {
svg {
shape-rendering: crispEdges;
}
}
Option 2: Simplify complex paths
Complex paths render differently across browsers. Simplify where possible.
Use SVG Minify to optimize paths and reduce complexity.
Option 3: Avoid browser-specific features
Some SVG features render inconsistently:
- Complex filters
- Nested clipping paths
- Mask combinations
Test these across browsers and simplify if needed.
Option 4: Provide fallbacks
<picture>
<source type="image/svg+xml" srcset="graphic.svg">
<img src="graphic.png" alt="Fallback">
</picture>
Fix 6: Animation Rendering Issues
The problem: SVG is sharp when static, blurry during or after animation.
Why It Happens
Animations often trigger GPU acceleration, which can rasterize the SVG. The rasterized version persists after animation ends.
How to Fix
Option 1: Force vector rendering after animation
svg {
animation: fadeIn 0.3s ease-out forwards;
}
@keyframes fadeIn {
from { opacity: 0; }
to {
opacity: 1;
transform: none; /* Reset any transforms */
}
}
Option 2: Animate transform carefully
/* Can cause blur */
svg {
animation: grow 0.3s ease-out;
}
@keyframes grow {
from { transform: scale(0.5); }
to { transform: scale(1); }
}
/* Better: animate container size */
.svg-container {
animation: grow 0.3s ease-out;
}
@keyframes grow {
from { width: 50px; height: 50px; }
to { width: 100px; height: 100px; }
}
Option 3: Use SMIL or CSS for SVG-native animation
Animations defined inside SVG often render better:
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40">
<animate attributeName="r" from="40" to="45" dur="1s" repeatCount="indefinite"/>
</circle>
</svg>
See SVG Animations Complete Guide for more techniques.
Additional Troubleshooting
Check the Source File
Sometimes the SVG itself is the problem:
- Exported at low quality from design tool
- Contains embedded raster images
- Has complex effects that don't translate well
Solution: Re-export from source at highest quality, or regenerate the graphic.
Optimize the SVG
Bloated SVGs with unnecessary complexity can render poorly:
# Use SVGO to optimize
npx svgo input.svg -o output.svg
Or use SVG Minify online.
Generate Clean SVGs
The cleanest way to avoid rendering issues is to start with well-structured SVG code.
SVG Genie generates optimized SVGs with:
- Clean path data (no unnecessary precision)
- Proper viewBox values
- No embedded raster content
- Cross-browser tested output
When troubleshooting isn't worth the time, regenerating the graphic often is.
Prevention Checklist
Avoid future blur issues:
- [ ] Use whole-number coordinates when possible
- [ ] Match viewBox aspect ratio to display size
- [ ] Test on retina displays during development
- [ ] Avoid CSS filters directly on SVGs
- [ ] Use
shape-rendering: crispEdgesfor UI elements - [ ] Optimize SVGs before deployment
- [ ] Test across browsers
Quick Reference
| Issue | Quick Fix |
|-------|-----------|
| Fuzzy lines | shape-rendering: crispEdges |
| Blurry on retina | Remove CSS filters, check transforms |
| Blurry after transform | Scale container, not SVG |
| Anti-aliasing fringes | shape-rendering: crispEdges or overlap shapes |
| Browser inconsistency | Simplify paths, test alternatives |
| Blurry animation | Animate container, not SVG directly |
Key Takeaways
- SVGs are vectors, but rendering is pixels — browser rendering decisions can cause blur
- Sub-pixel positioning is the #1 cause — use
shape-rendering: crispEdgesfor clean lines - CSS effects can rasterize SVGs — filters and 3D transforms trigger GPU layers
- Browsers differ — test across Chrome, Firefox, and Safari
- When in doubt, regenerate — clean SVG source prevents most issues
Sharp SVGs come from understanding how browsers translate vectors to pixels. Now you know how to fix it when things go wrong.
Want consistently sharp SVGs? SVG Genie generates production-ready graphics with clean code that renders crisply across all browsers and displays.
Related Articles:
Create your own SVG graphics with AI
Describe what you need, get a production-ready vector in seconds. No design skills required.