Bun.Image Makes Your Entire Image Pipeline Obsolete
BBetter Stack
Computing/SoftwareInternet Technology
Transcript
00:00:00This is Bun Image, a built-in image processing API shipped with Bun 1.3.14 that can resize,
00:00:06crop, and convert images between different formats with zero native dependencies,
00:00:10and the whole thing runs off the main thread, meaning it won't block your server while it's
00:00:14processing. But Bun is already a runtime, a package manager, a bundler, a test runner,
00:00:19and now it's an image processor? Is that all way too much, or is it telling us something
00:00:24bigger about where Bun is heading? Hit subscribe and let's find out.
00:00:30If you've ever processed images on the server with JavaScript, you may have used a library called
00:00:35Sharp without even knowing, which has more than 55 million downloads on npm a week, and it's even
00:00:41used by Next.js under the hood for image optimization. But Sharp relies on a native binary called libvips,
00:00:47which means every install has to pull the right build for your platform. And if you've ever had
00:00:51a Docker build or CI pipeline fail because of it, you know just how frustrating this is. So Bun decided
00:00:57to just build image processing into the runtime itself, and it's actually faster than Sharp in
00:01:02most benchmarks. Metadata reads as 70 times faster, and resizing is about 30% faster. Now a lot of
00:01:08developers are really confused as to why Bun added this, but I kind of see where this is all going,
00:01:13and I'll tell you a bit about that later. But for now, let's go through a few demos of how to use
00:01:17the Bun image API. And we're going to try it on my blog, which is a static Waku site, which has
00:01:22some huge images. To start things off, let's go through a simple script that optimizes one image,
00:01:27which is the profile picture with my face, and it's able to reduce the size by 99%, which is insane.
00:01:33Let's see how. First, we get the image, and note we're using Bun.image here, but you could also do
00:01:37Bun file with the image function. Then we resize the image to reduce its size, giving it a width of 800
00:01:43pixels, and the height is automatically calculated based on the aspect ratio. But if you wanted to,
00:01:47we could add that here as well. We then give it an output format of WebP with reduced quality. Of
00:01:52course, other output formats are supported, and then we output the new image to a file,
00:01:56which is a promise, and so it requires the await keyword. And you can see just how simple this is.
00:02:01In fact, all of this code here is unnecessary, so we could remove it and make the code even simpler.
00:02:06We could also set a resize filter, changing the resampling kernel, further reducing the image size.
00:02:10We could rotate or flip the image, and this is really cool. We could even change the brightness or
00:02:15saturation, which could lead to some weird looking images. But there is something really clever you
00:02:19can do when it comes to slow connections, which is to use the placeholder function to automatically
00:02:23show a blurry image while the main image is loading in. And to do that, I have this for loop that is
00:02:29going through every single file that contains these extensions inside the directory that contains all
00:02:34the block images. So each image is being resized, scaled down without being cropped, and not being
00:02:39upscaled. Then we create a placeholder for each image, which creates a thumb hash, meaning it encodes
00:02:45any image into a 28-byte hash, perfect as a blurry image placeholder.
00:02:49This hash is being added to a placeholder's object, and then it's been written to a file,
00:02:53which looks like this. Now, the reason the placeholder is a base64 image instead of a
00:02:58separate smaller WebP image is so that the network doesn't have to make a request to fetch it.
00:03:02So what I could do is import all the placeholders and add them as a background image in CSS,
00:03:07while waiting for the main image to load in, which will work for every image on my blog.
00:03:11But if you wanted to do that for a single image in an MDX file, you could just add the base64 code in
00:03:16manually, which works just as well. Note, you can also get a similar behavior using
00:03:20JPEG images by setting progressive to true. Of course, there are so many other things BUN image
00:03:24supports that I haven't yet gone through, like being able to get images from an S3 compatible
00:03:28bucket, or writing images to a bucket, using the image pipeline as a valid response body,
00:03:33and configuring a fallback format if a specific OS doesn't support one.
00:03:37So is this worth using? Well, if you're on BUN already, then yeah, this is a no-brainer.
00:03:41But if you're using Node, then Sharp works really well and is battle-tested,
00:03:45and there's no need to change to a completely different runtime just for image processing.
00:03:49Remember when I said there's a bigger reason that BUN added this? Well, if you look at what BUN's been
00:03:54doing over the last year, built-in SQLite, S3, Postgres, and now Images, and that's basically
00:03:59everything you need for a full-stack app, except for maybe auth and email. But this makes me wonder,
00:04:04is BUN trying to build the Laravel or Rails for JavaScript, but at the runtime level? If they are,
00:04:11then the next thing they're going to work on is auth. And if it is, remember, you heard it here first.
00:04:15But unfortunately, nowadays, you can't talk about BUN without mentioning the huge Zig to Rust
00:04:20rewrite, which, fingers crossed, will be coming out in the next version. Let's hope it all goes well.
00:04:25Speaking of Zig, if you want to learn more about Vercel's Language Zero that looks like Zig,
00:04:29but isn't Zig, and is built for AI agents, check out this video from James.