How Bluesky Draws Its Logo on Screenshots: A Vector Rendering Deep Dive
When you capture a screenshot of a Bluesky post and share it externally, the service doesn't just slap a pre-rendered PNG watermark onto the image. Instead, Bluesky dynamically redraws its iconic butterfly logo directly onto the screenshot canvas using a combination of vector paths, browser-native drawing APIs, and a few clever optimizations. This approach, which recently hit the front page of Hacker News, ensures the logo stays razor-sharp on anything from a 320px-wide thumbnail to a 5K desktop wallpaper.
The problem is one every social platform faces: how to attach a brand mark to user-generated content without introducing compression artifacts, color banding, or the dreaded "fuzzy edges" that come from scaling raster images. The answer in Bluesky's case is to treat the logo as code rather than as an image asset.
The Butterfly as a Path, Not a Picture
At the heart of Bluesky's system is an SVG path that describes the butterfly's two interlocking wings. The path is deliberately authored as a single continuous outline, with pairs of cubic Bézier curves defining the smooth, organic lobes. Instead of storing separate strokes or fills, the entire visual is one closed path with a fill rule—often evenodd—so inner cutouts (like the gap between the wings) are handled correctly.
The path data is compact enough to be embedded in JavaScript source. Here’s an example of the shape definition (simplified for clarity):
const BLUESKY_LOGO_PATH =
'M12.4 8.1C15.6 5.2 19.2 2.5 22.1 2.5' +
'c4.1 0 6.5 2.9 6.5 6.8 0 4.7-2.9 9.1-6.3 12.9' +
'C19.3 25.4 15 29.4 12 31.2c-3-1.8-7.3-5.8-10.3-9' +
'C-1.7 18.4-4.6 14-4.6 9.3-4.6 5.4-2.2 2.5 1.9 2.5' +
'c2.9 0 6.5 2.7 9.7 5.6z';
This path isn't used by an <svg> element alone. It's also parsed into a Path2D object, which the browser's Canvas API can directly consume. That means the same vector definition can be reused in two rendering pipelines: the regular web UI (via inline SVG) and the screenshot compositor (via Path2D and the 2D context).
Why Not Just a PNG?
Raster images seem like the obvious solution. Slap an bluesky-logo.png on a canvas and call it a day. But that approach has several fatal flaws for a platform that routinely handles images with different EXIF rotations, aspect ratios, and color spaces:
- Scale factor issues: On a 3x Retina display, a 128px PNG source gets upscaled, resulting in a blurry edge.
- Transparency cutting: PNGs store alpha in a way that can mix poorly with JPEG-encoded screenshots, producing halos or fringe pixels.
- File size overhead: A high-res PNG can be tens of kilobytes. The path data is under 200 bytes.
- Theme adaptability: Bluesky wants the logo to look good on both light and dark backgrounds. A static PNG requires a separate asset for each theme.
Vector rendering solves all these problems. The logo becomes a pure geometric description that can be scaled to any size, filled with any color, and even rotated to match the screenshot’s orientation.
The Compositing Pipeline
When a user taps "share screenshot," the client application kicks off a compositing routine. It doesn't just attach a blob. It follows a series of steps that are common in high-performance image processing, but rare in typical social apps:
1. Capture the raw screenshot
The app first obtains the raw screenshot data. On iOS and Android this is usually a UIGraphicsImageRenderer or PixelCopy call. On the web, Bluesky uses a custom html2canvas-style capture or a MediaStream from getDisplayMedia when the user is sharing a live view.
2. Create an offscreen canvas
The raw screenshot is drawn into an OffscreenCanvas at the native device pixel ratio. This is crucial. If the device has a DPR of 3, the canvas is created at 3x the logical pixel size. That way, the final exported image has full resolution.
const offscreen = new OffscreenCanvas(width * dpr, height * dpr);
const ctx = offscreen.getContext('2d');
ctx.scale(dpr, dpr); // Work in logical pixels, not physical ones
ctx.drawImage(screenshot, 0, 0, width, height);
3. Draw the logo with a Path2D object
Next, the logo is drawn not by calling an image loader, but by parsing the SVG path string into a Path2D and filling it. This happens in the same drawing context, so the logo is composited into the screenshot’s alpha channel.
const path = new Path2D(BLUESKY_LOGO_PATH);
const logoSize = 64;
const logoX = width - logoSize - 24;
const logoY = height - logoSize - 24;
ctx.translate(logoX, logoY);
ctx.scale(logoSize / 64, logoSize / 64);
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = 12;
ctx.fill(path);
Notice the shadow. Because the path is just geometry, the canvas API can apply the same effects you’d use on any vector shape. The shadow provides contrast when the screenshot has a light background. On a dark background, the client switches to a dark fill with a light shadow—all decided in real time by sampling the pixels near the logo position.
4. Export as a compressed image
Once the canvas has been fully rendered, the client converts it to a blob. To avoid the transparency issues mentioned earlier, Bluesky flattens the canvas onto a white background if the screenshot is JPEG-bound. It then uses toBlob with a quality setting that balances visual fidelity and file size.
const blob = await offscreen.convertToBlob({
type: 'image/jpeg',
quality: 0.92
});
For PNG output (when the user asks for lossless), the convertToBlob call produces a PNG with full alpha, but the canvas is flattened first to prevent huge file sizes.
Handling High-DPI and Multiple Aspect Ratios
One of the more interesting details from the HN discussion is how Bluesky deals with screenshots of varying dimensions. The logo isn’t always drawn in the bottom-right corner. Instead, the compositing logic uses a simple layout system:
- If the screenshot is portrait (height > width), place the logo at the bottom centered.
- If it’s landscape, place it at the top-right and rotate 90 degrees, so it reads vertically.
- If it’s square, scale the logo to 80% of the shortest edge and center it.
The positioning code is pure math, but the scaling uses a relative factor. The author of the HN post pointed out that the logo’s size is always a function of the shortest edge, never a fixed pixel value. That means a 500×1000 pixel screenshot gets a proportionally smaller logo than a 1000×2000 one, preserving visual balance.
Performance: Offscreen Canvas and Web Workers
The compositing pipeline could easily block the main thread on lower-end devices, especially with a 12-megapixel screenshot. Bluesky avoids this by doing all the drawing in a Web Worker. OffscreenCanvas supports transferring the bitmap from the main thread to the worker, so the rendering happens without jank.
Here’s a simplified worker message flow:
// worker.js
self.onmessage = (e) => {
const { imageBitmap, width, height, dpr } = e.data;
const canvas = new OffscreenCanvas(width * dpr, height * dpr);
const ctx = canvas.getContext('2d');
// ... draw screenshot, draw path, export
self.postMessage({ blob });
};
The P2D path string is initialized once in the worker, not on every message. That cuts down on parsing overhead. The worker also caches the path’s bounding box and computes the appropriate translation and scale matrices ahead of time.
Why This Technique Is a Template for Other Apps
Bluesky’s approach is noteworthy not because it's esoteric, but because it's one of the first mainstream examples of a brand mark being drawn entirely at runtime using geometric primitives. The implications are broad:
- Brand consistency: The logo is the same mathematical object across every platform. No more mismatched assets.
-
Accessibility: Since the logo is a vector, it can be augmented with accessibility metadata, like
aria-label, without extra load. - Theming: The logo inherits CSS variables or runtime colors, so dark mode isn’t a separate asset.
- Future-proofing: If the logo is ever updated, only the path string changes. No CDN purge, no cache-busting query parameters.
A Minimal Example You Can Use
Want to do the same in your own app? Here’s a tiny, self-contained function that draws a vector logo on a screenshot using the same core principles:
async function drawLogoOnScreenshot(screenshot, dpr = window.devicePixelRatio) {
const { width, height } = screenshot;
const canvas = new OffscreenCanvas(width * dpr, height * dpr);
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.drawImage(screenshot, 0, 0);
// Your logo path here
const path = new Path2D('M1 1 L10 1 L10 10 Z');
const s = Math.min(width, height) * 0.1;
ctx.translate(width - s - 16, height - s - 16);
ctx.scale(s / 10, s / 10);
ctx.fillStyle = 'white';
ctx.shadowColor = 'black';
ctx.shadowBlur = 8;
ctx.fill(path);
return await canvas.convertToBlob({ type: 'image/png' });
}
Conclusion
The next time you see the Bluesky butterfly float over a shared screenshot, remember that it’s not a static image. It’s a carefully authored Bézier path that’s parsed, scaled, and filled at runtime, all inside a GPU-accelerated canvas. This engineering choice gives Bluesky one of the crispest, most adaptable brand presentations on the modern social web—and it’s a pattern that likely points toward how more apps will handle dynamic overlays in the future.
Because in a world where screenshots range from smartwatch faces to 8K monitors, the only way to win is with math.
Top comments (0)