SVG is a vector image format that stays crisp at any size and plays nicely with CSS and JavaScript — which makes it feel like the obvious choice for icons and graphics. Yet "I set an SVG as my OGP image and nothing showed up" or "my SVG icon doesn't appear in Slack" are surprisingly common frustrations.

The reality is that a long list of platforms simply won't accept SVG, and for those you need PNG. This article covers which platforms reject SVG and why, and walks through how a browser converts SVG to PNG entirely client-side using the Canvas API.

Platforms That Reject SVG

Setting og:image to an SVG URL doesn't work on Facebook, X (Twitter), LINE, or Discord — none of them render it as a preview. The Open Graph Protocol specification doesn't restrict image formats, but every major social platform processes only JPEG and PNG when generating link previews.

Email clients have the same problem. Embedding <img src="*.svg"> in an HTML email fails silently in many clients, and Outlook — which still commands a large share of enterprise email — has no SVG rendering support at all.

iOS home screen shortcut icons (apple-touch-icon) require PNG. Slack custom emoji and workspace icons accept PNG, GIF, and JPEG, but not SVG.

Two reasons recur across all of these. First, platforms that resize and cache images server-side typically don't include an SVG rasterizer in that pipeline. Second, SVG can embed JavaScript and reference external resources, which makes it a potential XSS vector in any context where users can supply images — so it gets blocked preemptively.

Vectors vs. Pixels

SVG is a set of drawing instructions: "draw a circle at these coordinates with radius 20," "trace this path." Because the shapes are defined mathematically, scaling from 256×256 to 4000×4000 is just recalculation — no quality loss.

PNG is a grid of pixels. A 256×256 PNG contains exactly 256 rows of 256 color values. Scale it up and you're stretching individual pixels, which produces the blurry edges familiar from low-resolution images.

The underlying reason platforms need PNG comes down to this: their image pipelines work on pixels. Whether generating a thumbnail, caching a preview, or compositing an image, they need pixel data — and SVG doesn't provide that directly.

How the Browser Converts SVG to PNG

The conversion runs entirely in the browser through four steps using the Canvas API.

Step 1: Wrap the SVG string in a Blob and create a local URL

const svgBlob = new Blob([svgCode], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(svgBlob);

URL.createObjectURL() returns a blob:https://... URL that points to the in-memory Blob. Blob URLs are treated as same-origin local resources, which sidesteps the Canvas tainted-canvas restriction. If you draw a cross-origin image directly onto a Canvas, calling toDataURL() afterward throws a security error — routing through a Blob URL avoids this.

Step 2: Load the Blob URL into an <img> element

const img = new Image();
img.onload = () => { /* proceed to step 3 */ };
img.src = url;

There's no API to hand an SVG string directly to a Canvas context, so this step lets the browser parse and render the SVG internally. By the time onload fires, the browser has rasterized the SVG into an in-memory bitmap.

Step 3: Draw onto the Canvas with drawImage()

const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);

drawImage() writes the rasterized SVG into the Canvas pixel buffer. The Canvas dimensions set the output resolution. Call revokeObjectURL() once done to free the memory.

Step 4: Export the Canvas as PNG

const pngDataUrl = canvas.toDataURL('image/png');

toDataURL('image/png') compresses the Canvas pixel buffer into a PNG and returns it as a data:image/png;base64,... string. From there you can set it as the href of a download link, or convert it to a Blob via fetch() and write it to the clipboard using the Clipboard API.

Why Retina Displays Need 2× Output

On a Retina or High-DPI display, one CSS pixel maps to two (or more) physical pixels — this ratio is called the device pixel ratio (DPR). A 256×256 PNG displayed at width: 256px on a DPR-2 screen gets stretched to 512×512 physical pixels, making edges visibly soft.

The fix is to render the Canvas at 512×512 (or whatever target × DPR works out to) and display it at width: 256px. SVG sidesteps this entirely by recalculating at render time, but once you're outputting PNG you have to commit to a resolution upfront. For OGP images, each platform publishes a recommended size — Facebook's is 1200×630px — and hitting that size ensures the image is displayed without any upscaling.

Transparent Backgrounds Survive PNG Conversion

SVG has no background by default, and PNG supports an alpha channel, so transparency carries through the conversion. JPEG has no alpha channel, which is why transparent-background assets always end up as PNG rather than JPEG.

To preserve transparency through the Canvas conversion, simply skip the fillRect() call. The Canvas pixel buffer is initialized with every pixel at alpha 0 (fully transparent black) — leave it alone, and toDataURL('image/png') produces a PNG with a transparent background.

The practical split is straightforward: for Slack emoji, icons, or logo assets where transparency matters, skip the background fill. For OGP images, favicons, or anything displayed against a specific background, call fillRect() with the desired color before drawing the SVG.

To preview SVG code and export it as PNG directly in the browser — with controls for canvas size, background color, Retina scale, and transparency — try the tool below.