npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dct-thumbnail

v2.0.0

Published

Thumbnails of images that fit in a few dozen bytes, to show while the image loads

Readme

dct-thumbnail

Thumbnails of images that fit in a few dozen bytes, to show in their place while the image loads.

A thumbnail keeps the lowest frequencies of the image, the ones that say what it looks like from far away. It's small enough to go in the HTML next to the image, so there's something to show before anything is downloaded.

npm

Demo

2026 update

Disclaimer: this library is from 2017. The same idea has become two formats that are widely used and have encoders and decoders for many languages: BlurHash (2018) and ThumbHash (2023). Use one of those for anything new

Browsers also load images lazily by themselves since 2019, with loading="lazy", which is what this library used to do with its own code.

The library has been rewritten, and what version 1 made can't be read by this one. What changed:

  • it's two ES modules with no dependencies. Version 1 needed an old version of the canvas package for Node, and its encoder didn't work in a browser
  • a thumbnail is 49 bytes, a string of 68 characters that goes in an attribute of the image. It was a PNG file to download, of 16 x 4 pixels, of which only a quarter was used
  • the encoder works in the browser, from an image, a canvas or a video, and anywhere else from the pixels
  • a thumbnail can be decoded at any size, and it says how many frequencies it has, from 1 to 16 along each side
  • the image is a plain <img>, that works without scripts and loads lazily with loading="lazy". It was a <div> with another inside, a <noscript> and the loading done on scroll
  • the command line tool is gone. The demo makes the thumbnails, and it takes a few lines to make them in Node

How to use

Use npm to install it

npm i dct-thumbnail

It's two plain ES modules, so they also work without a bundler (see demo/index.html).

Make the thumbnail of an image. This is done once, when the image is added to the site, with the demo or with your own code:

import { encode, toBase64 } from 'dct-thumbnail';

const thumb = toBase64( encode( image ) );
// 'BFRGM4qFgXeAh3BzeqizlXd5fYiOh4KAf3qDhH+AgoSGg3x/gYOGhYaGgYqAf4WDgQ=='

Put it in the image, as data-thumb. Give the image its size, so the page has its place before it loads:

<img src="mona-lisa.jpg" width="687" height="1024" loading="lazy"
  data-thumb="BFRGM4qFgXeAh3BzeqizlXd5fYiOh4KAf3qDhH+AgoSGg3x/gYOGhYaGgYqAf4WDgQ==">

And in the page, call applyThumbnails() once the images are there:

import { applyThumbnails } from 'dct-thumbnail/loader';

applyThumbnails();

Every image with a data-thumb that hasn't loaded yet shows its thumbnail as its background, which goes away when the image loads. It returns the images it did it to. Pass an element to look only inside of it, and call it again when images are added to the page: the ones that were done are skipped.

By default the image replaces its thumbnail as soon as it has loaded. To have it fade in, pass how long it takes, in milliseconds:

applyThumbnails( document, { fade: 500 } );

Encoding

encode( source, coefficients ) returns the thumbnail as a Uint8Array.

source is an image, a canvas, a video or an ImageData. It can also be { data, width, height } with the RGBA pixels of the image, which is how to use it where there's no page, like Node or a worker. With sharp, for instance:

import sharp from 'sharp';
import { encode, toBase64 } from 'dct-thumbnail';

const { data, info } = await sharp( 'mona-lisa.jpg' ).ensureAlpha().raw().toBuffer( { resolveWithObject: true } );
const thumb = toBase64( encode( { data, width: info.width, height: info.height } ) );

coefficients is how many frequencies are kept along each side, from 1 to 16, and 4 by default. A thumbnail has 1 + 3 * coefficients * coefficients bytes: 49 with 4, 193 with 8. With 1 it's just the average color.

toBase64( thumb ) and fromBase64( string ) turn the bytes into a string and back.

Decoding

Everything that takes a thumbnail takes the bytes or the string.

  • decode( thumb, width, height ) returns its picture as { data, width, height }, with RGBA pixels. It's 32 x 32 by default, and it can be any size: the picture is smooth at all of them, so a small one stretched by the page looks the same.
  • toCanvas( thumb, width, height ) returns it in a canvas, and toDataURL( thumb, width, height ) as the URL of a PNG.
  • averageColor( thumb ) returns the average color of the image, like #544633.

The format

The image is reduced to 16 x 16 pixels, and transformed with a discrete cosine transform, the one that JPEG uses. The first byte of a thumbnail is the number of frequencies along each side. Then, by rows, come three bytes for each frequency, with its amount of red, green and blue plus 128. The first one is the average color of the image.

The transparency of the image is not kept, and neither is its shape: a thumbnail is stretched to the size it's shown at.

License

MIT licensed

Copyright (C) 2017 Jaume Sanchez Elias, https://www.clicktorelease.com