Seam carving: content-aware image resizing … in JavaScript

Oleksii Trekhleb has implemented the Seam Carving algorithm in JavaScript.

With this article I want to do three things:

  1. Provide you with an interactive content-aware resizer so that you could play around with resizing your own images
  2. Explain the idea behind the Seam Carving algorithm
  3. Explain the dynamic programming approach to implement the algorithm (we’ll be using TypeScript for it)

Using the algorithm it’s possible to resize an original image (center) without distorting important features in the image (left) unlike regular resizing which squeezes the image (right).

Using the resulting JS Image Carver you can see the algorithm in action.

Content-Aware Image Resizing in JavaScript →
JS Image Carver →
JS Image Carver Source (GitHub) →

Display a BlurHash using only CSS thanks to blurhash-to-css

If you want to display Blurhash Placeholder Images on your site you need a JavaScript solution to do so. The blurhash-to-css package bypasses that, as it allows you to convert a Blurhash to a set of CSS properties that need to apply.

import { blurhashToCss } from "blurhash-to-css";

const css = blurhashToCss(
  "eCF6B#-:0JInxr?@s;nmIoWUIko1%NocRk.8xbIUaxR*^+s;RiWAWU"
);
{
  "backgroundImage": "linear-gradient(90deg, rgb(139,153,96) 10%,rgb(153,160,118) 10% 20%,rgb(170,172,142) 20% 30%,rgb(177,180,151) 30% 40%,rgb(174,180,146) 40% 50%,rgb(162,172,130) 50% 60%,rgb(148,162,114) 60% 70%,rgb(136,150,104) 70% 80%,rgb(131,145,95) 80% 90%,rgb(130,144,91) 90% 100%),linear-gradient(90deg, rgb(109,129,65) 10%,rgb(117,131,80) 10% 20%,rgb(130,138,101) 20% 30%,rgb(142,148,113) 30% 40%,rgb(146,150,114) 40% 50%,rgb(136,144,103) 50% 60%,rgb(116,130,88) 60% 70%,rgb(102,119,75) 70% 80%,rgb(104,118,71) 80% 90%,rgb(112,123,72) 90% 100%),linear-gradient(90deg, rgb(110,122,70) 10%,rgb(114,122,85) 10% 20%,rgb(126,129,106) 20% 30%,rgb(143,140,122) 30% 40%,rgb(151,147,128) 40% 50%,rgb(144,142,122) 50% 60%,rgb(123,126,107) 60% 70%,rgb(106,111,90) 70% 80%,rgb(108,109,83) 80% 90%,rgb(118,117,83) 90% 100%),linear-gradient(90deg, rgb(134,133,102) 10%,rgb(141,138,120) 10% 20%,rgb(155,150,144) 20% 30%,rgb(172,164,161) 30% 40%,rgb(179,173,168) 40% 50%,rgb(173,168,162) 50% 60%,rgb(156,151,146) 60% 70%,rgb(137,132,127) 70% 80%,rgb(127,124,110) 80% 90%,rgb(128,124,101) 90% 100%),linear-gradient(90deg, rgb(116,124,95) 10%,rgb(125,129,112) 10% 20%,rgb(143,141,135) 20% 30%,rgb(160,156,153) 30% 40%,rgb(169,164,160) 40% 50%,rgb(165,161,155) 50% 60%,rgb(148,145,141) 60% 70%,rgb(124,125,118) 70% 80%,rgb(107,110,97) 80% 90%,rgb(99,106,82) 90% 100%),linear-gradient(90deg, rgb(95,105,51) 10%,rgb(99,105,66) 10% 20%,rgb(110,111,88) 20% 30%,rgb(125,122,107) 30% 40%,rgb(136,131,115) 40% 50%,rgb(134,129,111) 50% 60%,rgb(120,116,98) 60% 70%,rgb(97,97,77) 70% 80%,rgb(78,84,51) 80% 90%,rgb(70,78,30) 90% 100%),linear-gradient(90deg, rgb(109,106,21) 10%,rgb(109,104,44) 10% 20%,rgb(112,104,67) 20% 30%,rgb(122,109,83) 30% 40%,rgb(131,114,88) 40% 50%,rgb(130,112,83) 50% 60%,rgb(119,103,70) 60% 70%,rgb(104,91,51) 70% 80%,rgb(93,84,34) 80% 90%,rgb(92,84,24) 90% 100%)",
  "backgroundPosition": "0 0 ,0 16.666666666666664%,0 33.33333333333333%,0 50%,0 66.66666666666666%,0 83.33333333333334%,0 100%",
  "backgroundSize": "100% 14.285714285714286%",
  "backgroundRepeat": "no-repeat",
  "filter": "blur(24px)",
  "transform": "scale(1.2)"
}

Integrate this conversion on build/upload time and you’re good to go 🙂

Installation per NPM:

npm install blurhash-to-css

blurhash-to-css
blurhash-to-css Demo Page →

Writing Good Alt Text

Jake and Surma tackle the age-old problem: what should you include in an image’s alt text?

Easily include Blurhash placeholders in your React projects with react-blurhash

react-blurhash allows you to easily integrate Blurhash Placeholder Images images in your React Projects:

Blurhash component is the recommended way to render blurhashes in your React projects. It uses BlurhashCanvas and a wrapping div to scale the decoded image to your desired size. You may control the quality of the decoded image with resolutionX and resolutionY props.

Installation per NPM

npm install --save blurhash react-blurhash

Example Usage:

import './App.css';
import { Blurhash } from "react-blurhash";


function App() {
  return (
    <div className="App">
      <Blurhash
        hash="eCF6B#-:0JInxr?@s;nmIoWUIko1%NocRk.8xbIUaxR*^+s;RiWAWU"
        width={600}
        height={400}
      />

      <img
        src="https://example.org/original.jpg"
        width={600}
        height={400}
      />
    </div>
  );
}

export default App;

react-blurhash (GitHub) →

Alt vs Figcaption

This message by Elaina Natario writing over at Thoughtbot cannot be repeated enough:

While both the alt attribute and the figcaption element provide a way to describe images, the way we write for them is different. alt descriptions should be functional; figcaption descriptions should be editorial or illustrative.

Examples of both functional and editorial descriptions in the full post!

Alt vs Figcaption →

Monochrome Image Dithering Explained

Surma digging into the oldskool dithering technique:

I always loved the visual aesthetic of dithering but never knew how it’s done. So I did some research. This article may contain traces of nostaliga and none of Lena.

Turns out there’s quite a lot to it 😅

Ditherpunk — The article I wish I had about monochrome image dithering →
Ditherpunk Demo Page →

Compress and Convert AVIF/WebP/PNG/etc images on the CLI with squoosh-cli

To compress and compare images with different codecs right in your browser there’s squoosh.app that you can use.

Announced at the still ongoing Chrome Dev Summit 2020 is Squoosh v2 with new codecs support (AVIF!), an updated design, and the release of CLI version!

Squoosh CLI is an experimental way to run all the codecs you know from the Squoosh web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.

Squoosh CLI is currently not the fastest image compression tool in town and doesn’t aim to be. It is, however, fast enough to compress many images sufficiently quick at once.

Run it using npx, or install it globally:

npx @squoosh/cli <options...>
npm i -g @squoosh/cli
squoosh-cli <options...>

Announcing Squoosh v2 →
Squoosh CLI (Repo) →

BlurHash — Low-res Blurred Placeholder Images Represented as Text

If you’re dealing with images it’s quite common to show a small placeholder while the image is loading. You could go with grey placeholders, but a low-res blurred version of the original is preferred. That way you can, in the example use case of a website, use the Blur Up technique once the image is loaded. BlurHash is something that can help you with exactly that:

In short, BlurHash takes an image, and gives you a short string (only 20-30 characters!) that represents the placeholder for this image. The string is short enough that it comfortably fits into whatever data format you use. For instance, it can easily be added as a field in a JSON object.

An example of a BlurHash would be LEHV6nWB2yk8pyo0adR*.7kCMdnj

Implementations that can encode and decode exist for TypeScript, PHP, Python, Swift, Kotlin, etc.

To use BlurHashes in the context of a web browser without needing to rely on JavaScript on the client side, I’d use this with a Cloud Function (or the like) that converts the encoded version to the actual image. Your markup could then look something like this:

<span style="display: inline-block; background: transparent url('https://blurhash-decoder.function.cloud/?blurhash=LEHV6nWB2yk8pyo0adR%2A.7kCMdnj') 0 0 / 100% 100%;">
	<img src="https://example.com/assets/original.jpg" width="538" height="" alt="346" title="" />
</span>

To tone down the potential number of network requests you could of course pre-decode those BlurHashes on the server and inject the background images using Data URIs from within your template engine.

BlurHash →

How to embed AV1 Image File Format (AVIF) images

New in Chromium 85 is support for the AV1 Image File Format (AVIF), which is pretty impressive:

AVIF offers significant file size reduction for images compared with JPEG or WebP; ~50% savings compared to JPEG, and ~20% savings compared to WebP.

🦊 Using Firefox and can’t wait to use AVIF images? Set the image.avif.enabled flag to true to enable experimental support for it.

Time to tweak the modern way to embedding images a bit, and add AVIF in there:

<picture>
  <source srcset="/images/cereal-box.avif" type="image/avif" />
  <source srcset="/images/cereal-box.webp" type="image/webp" />
  <img src="/images/cereal-box.jpg" alt="Description of Photo" />
</picture>

The browser will load the first source it can interpret, eventually falling back to the JPG if none are supported.

☝️ Now that Safari is about to support WebP in version 14, the image/jp2 image that was in the original snippet was also dropped.

How to Use AVIF: The New Next-Gen Image Compression Format →

UPDATE 2020.09.08: Jake Archibald just released an extensive post on AVIF packed with examples and comparisons, worth checking out.

Native Image Lazy-Loading: loading-attribute-eagle-polyfill

Today, Rick Viscomi noted that some sites have set eagle – instead of eager – as the value for Native Image Lazy-Loading:

While this is most likely a classic case of #damnyouautocorrect (instead of jokingly being a LOTR/Scrubs reference), that didn’t keep Jay Phelps from creating loading-attribute-eagle-polyfill to cater for those small mishaps:

A polyfill for <img loading="eagle" />. Displays an America Eagle as the placeholder of the image while the your real images are still loading.

LOL 😁 — I love the internet.

Here’s a code example on how to use, if you ever were to use it in the first place:

<head>
  <script src="https://unpkg.com/loading-attribute-eagle-polyfill/loading-attribute-eagle-polyfill.js"></script>
</head>
<body>
  <!-- Here's an example URL that artificially delays the src so you can see the proud Eagle -->
  <img
    loading="eagle"
    src="https://deelay.me/2000/https://img.webmd.com/dtmcms/live/webmd/consumer_assets/site_images/article_thumbnails/other/cat_relaxing_on_patio_other/1800x1200_cat_relaxing_on_patio_other.jpg"
    width="300"
    height="200"
  />
</body>

loading-attribute-eagle-polyfill

ℹ️ Remember Native Image Lazy Loading being way too eager? Chrome recently updated the thresholds and are backporting the changes back to Chrome version 79: