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

inky-impression-73

v1.0.0

Published

Minimal Node.js driver for the Pimoroni Inky Impression 7.3-inch Spectra 6 display

Readme

Inky Impression 7.3 for Node.js

Hello! This is a small, focused Node.js driver for one display:

  • Pimoroni Inky Impression 7.3-inch
  • 800x480 pixels
  • E Ink Spectra 6 panel
  • E673 display controller
  • Raspberry Pi 5 or Raspberry Pi Zero 2 W

It is a focused JavaScript port of the relevant E673 path in Pimoroni's MIT-licensed Python inky library. The controller constants and command sequence are derived from that project, whose copyright notice is retained in LICENSE.

The upstream project is available at pimoroni/inky.

There is no Python application, virtual environment, Pillow, or NumPy. The low-level driver accepts an exact-size RGBA frame, maps it to the panel's six colors, packs two pixels per byte, and sends the resulting 192,000-byte framebuffer over SPI.

The included inky command uses canvas-plus to load common image formats, correct EXIF orientation, and resize or crop them to exactly 800x480 pixels. It then uses image-q to apply selectable error-diffusion dithering against the display's exact six-color palette.

Requirements

  • Raspberry Pi 5, Raspberry Pi Zero 2 W, or Raspberry Pi Zero 2 WH
  • Current 64-bit Raspberry Pi OS based on Debian 13 Trixie
  • Node.js 20 or newer. Node.js 24 is recommended.
  • Inky Impression 7.3-inch Spectra 6 connected to the 40-pin header

This driver does not support the older seven-color AC073TC1A version of the 7.3-inch display.

The standard Zero 2 W has an unpopulated GPIO header. It requires a soldered 40-pin header or another reliable header adapter. The Zero 2 WH includes the header from the factory. Raspberry Pi OS Lite 64-bit is recommended for the Zero 2 W because it leaves more of the board's 512 MB of RAM available for image decoding and dithering.

Automatic installation

The recommended installer configures a supported Raspberry Pi from beginning to end:

curl -fsSL https://raw.githubusercontent.com/jhuckaby/inky-impression-73/main/install.sh | sudo bash

It verifies the computer model, Debian release, and CPU architecture; installs the required Debian packages; enables SPI with the correct chip-select overlay; installs Node.js 24 from NodeSource when a compatible Node.js is not already present; and installs the global inky command. It is safe to run the installer again. Existing active settings and compatible software are left in place.

The installer creates /boot/firmware/config.txt.inky-backup before its first boot-configuration change. It does not reboot the Pi automatically.

If you prefer to inspect a root-level script before running it, download it first:

curl -fsSL https://raw.githubusercontent.com/jhuckaby/inky-impression-73/main/install.sh -o install.sh
less install.sh
sudo bash install.sh

Manual Raspberry Pi setup

The display uses SPI0 plus four GPIO lines. Pimoroni's driver controls chip select itself, so SPI0 must not claim GPIO8 as a hardware chip-select line. The driver relies on the spi0-0cs overlay on both supported Pi families. It does not request the SPI_NO_CS mode bit, which the Pi 5 RP1 SPI controller rejects.

The commands below use sudo. If you are already logged in as root, omit it.

In /boot/firmware/config.txt, make sure these lines are present under an applicable section such as [all]:

dtparam=spi=on
dtoverlay=spi0-0cs

Reboot after changing the boot configuration:

sudo reboot

After rebooting, verify that the SPI device exists:

ls -l /dev/spidev0.0

Install the native build prerequisites:

sudo apt update
sudo apt install build-essential python3 pkg-config gpiod libatomic1 libgpiod-dev \
	libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev

The npm packages contain native Node.js bindings, so installation uses the system Python executable through node-gyp as a compiler helper. It does not create a virtual environment, install Python packages, or require Python when the driver runs.

Installing the command globally

Install the published package globally with this command:

sudo npm install --global \
	--allow-scripts=canvas,exifreader,opengpio,spi-device \
	--build-from-source=canvas \
	inky-impression-73

That single command installs the inky executable, approves the four reviewed native installers for npm 12's global-install policy, and compiles only the Canvas dependency from source. There is no separate Canvas rebuild step.

Compiling Canvas locally provides one reliable installation path for both supported computers. It is especially important on Raspberry Pi 5 systems with a 16 KB kernel page size, where the downloaded ARM64 Canvas binary may contain a bundled Cairo library built with incompatible ELF alignment. The installation command above skips that binary and links Canvas against Raspberry Pi OS system libraries from the beginning. npm 12 may warn that --build-from-source is an unknown npm option, but it still passes the option to the Canvas installer.

The project pins all four reviewed native build scripts in the npm allowScripts field: [email protected], [email protected], [email protected], and [email protected].

Verify the global command after installation:

inky --version
inky --help

If your npm global prefix belongs to your normal user, you may omit sudo. Do not run the inky command itself with sudo; membership in the gpio and spi groups should provide the required hardware access.

First display test

Display a local photograph or other image file:

inky photograph.jpg

A full Spectra 6 refresh takes roughly 30 to 40 seconds. Do not interrupt power while the panel is refreshing.

Displaying an image from the command line

The default behavior preserves the image's aspect ratio, centers it, and adds white padding wherever necessary:

inky photograph.jpg

Corrected cumulative-error Floyd-Steinberg dithering is enabled by default. This mixes the six physical panel colors to approximate the many colors and shades in a photograph. Serpentine scanning alternates direction on each row to reduce directional artifacts.

Three alternative error-diffusion modes are available for experimentation:

inky photograph.jpg --dither-type floyd-steinberg
inky photograph.jpg --dither-type sierra
inky photograph.jpg --dither-type atkinson
inky photograph.jpg --dither-type riemersma

Sierra spreads error across a wider neighborhood and can look smoother in photographs. Atkinson preserves only part of the error and often produces a cleaner, higher-contrast result. Riemersma follows a Hilbert curve instead of scanning horizontal rows, which avoids obvious row-oriented patterns.

Disable dithering for logos, diagrams, or artwork already designed for the native palette:

inky logo.png --no-dither

The padding color accepts CSS colors, #RRGGBB, or bare RRGGBB values:

inky poster.png --background '#000000'
inky poster.png --background FFF4DC

Three fitting modes are available:

# Preserve the complete image and pad it to 800x480. This is the default.
inky photograph.jpg --fit contain

# Fill the complete display and crop overflow from the edges.
inky photograph.jpg --fit cover

# Ignore the original aspect ratio and stretch to exactly 800x480.
inky photograph.jpg --fit stretch

Use gravity to control alignment for padding or cropping:

inky portrait.webp --fit cover --gravity north

Valid gravity values are center, northwest, north, northeast, east, southeast, south, southwest, and west.

The fixed Spectra palette blend defaults to 0.25, based on testing with the physical display. A value of 0 uses idealized RGB colors, while 1 uses Pimoroni's measured, more subdued palette:

inky photograph.jpg --palette-blend 0.5

Source-image saturation and contrast are separate canvas-plus adjustments. Both use canvas-plus's native -255 to 255 scale. The CLI defaults both to 32, a photographic preset selected after testing varied images on the physical Spectra 6 panel:

inky photograph.jpg --saturation 64
inky photograph.jpg --contrast 16

# Disable both default photographic adjustments for neutral preprocessing.
inky artwork.png --saturation 0 --contrast 0

The lower-level prepareImage() function remains neutral by default. Programmatic callers must explicitly pass saturation and contrast if they want the CLI's photographic preset.

For the complete command reference:

inky --help

Displaying RGBA pixels

To use the driver from your own Node.js application, install it in that application rather than relying on the global CLI package. With npm 12, first add the same reviewed native installers to your application's package.json:

{
	"allowScripts": {
		"[email protected]": true,
		"[email protected]": true,
		"[email protected]": true,
		"[email protected]": true
	}
}

Then install the package:

npm install --build-from-source=canvas inky-impression-73

The --build-from-source=canvas option provides the same reliable Canvas installation on both supported computers. It does not force the other dependencies to compile differently.

Pass exactly 800x480x4 bytes in normal Canvas order: red, green, blue, alpha, from the top-left pixel across each row.

const { InkyImpression73 } = require("inky-impression-73");

async function updateDisplay(rgba) {
	const display = new InkyImpression73();
	
	try {
		await display.show(rgba);
	}
	finally {
		display.close();
	}
}

rgba may be a Buffer, Uint8Array, Uint8ClampedArray, or an ImageData-like object containing data, width, and height properties.

The default color matching uses the hardware-tested 0.25 palette blend. You can choose a value from 0 through 1:

await display.show(rgba, { paletteBlend: 0.5 });

Error-diffusion dithering is available for programmatic RGBA input as an option:

await display.show(rgba, {
	paletteBlend: 0.25,
	dither: true,
	ditherType: "sierra"
});

Valid ditherType values are exported as DITHER_TYPES. They are floyd-steinberg, sierra, atkinson, and riemersma. Floyd-Steinberg is the default.

Transparent and partially transparent pixels are composited over white. This low-level RGBA converter deliberately does not resize, crop, or rotate the image. Those optional preprocessing steps live in prepareImage() and the CLI.

For programmatic image preparation, prepareImage() exposes the same canvas-plus pipeline used by the CLI:

const { InkyImpression73, prepareImage } = require("inky-impression-73");

async function displayFile(filename) {
	const rgba = await prepareImage(filename, {
		fit: "contain",
		background: "#FFFFFF",
		gravity: "center",
		saturation: 32,
		contrast: 32
	});
	const display = new InkyImpression73();
	
	try {
		await display.show(rgba);
	}
	finally {
		display.close();
	}
}

If you already produce the controller's packed format, showPacked() accepts exactly 192,000 bytes and sends them without pixel conversion.

Using native color indexes

If the source already uses the six panel colors, you can skip RGB matching. Supply one byte per pixel using these exported codes:

const {
	COLORS,
	InkyImpression73,
	PIXEL_COUNT
} = require("inky-impression-73");

const pixels = Buffer.alloc(PIXEL_COUNT, COLORS.WHITE);

pixels[0] = COLORS.BLACK;
pixels[1] = COLORS.YELLOW;
pixels[2] = COLORS.RED;
pixels[3] = COLORS.BLUE;
pixels[4] = COLORS.GREEN;

async function showNativePixels() {
	const display = new InkyImpression73();
	
	try {
		await display.showColorIndexes(pixels);
	}
	finally {
		display.close();
	}
}

showNativePixels().catch(console.error);

The native codes are BLACK, WHITE, YELLOW, RED, BLUE, and GREEN. Code 4 is not valid on this controller.

Advanced options

The defaults match the Pimoroni HAT and E673 Python driver:

const display = new InkyImpression73({
	spiBus: 0,
	spiDevice: 0,
	speedHz: 1_000_000,
	gpioChip: undefined,
	pins: {
		reset: 27,
		busy: 17,
		dc: 22,
		cs: 8
	}
});

The driver normally discovers the 40-pin header's GPIO chip by its stable controller label, first through sysfs and then through gpiodetect as a fallback. It recognizes RP1 on Pi 5 and the Broadcom pin controller on Zero 2 W. Set gpioChip only if both automatic methods fail and gpiodetect shows the correct chip number.

Scope

This deliberately minimal port includes:

  • Command-line JPEG, PNG, GIF, and WebP loading
  • EXIF auto-orientation, resizing, cropping, and padding through canvas-plus
  • Source-image saturation and contrast adjustment through canvas-plus
  • Fixed-palette, selectable error-diffusion dithering through image-q
  • RGBA-to-Spectra-6 nearest-color mapping
  • Direct native color-index input
  • Direct pre-packed E673 framebuffer input
  • E673 initialization, power, refresh, and shutdown sequences
  • Pi 5 and Zero 2 W GPIO through libgpiod 2.x
  • SPI transfer chunking
  • Mockable hardware boundaries and protocol tests

It intentionally omits:

  • Other Inky models or controller families
  • EEPROM model detection
  • Text, fonts, and drawing primitives
  • Buttons and the activity LED
  • Partial refresh, which this color panel does not support through this driver

Testing

To work on the source code, clone the repository and install its dependencies locally. On either supported Raspberry Pi, use the same targeted Canvas source build:

npm install --build-from-source=canvas
npm test

The source checkout also includes a six-color hardware test:

npm run example:stripes

You can run the CLI directly from the source directory with ./cli.js photograph.jpg, or expose the checkout as the global inky command with sudo npm link.

The unit tests do not access GPIO or SPI, so they can also run on a development Mac:

npm install
npm test

The tests verify CLI parsing, image preprocessing, palette blending, dithering, color codes, nibble packing, framebuffer size, RP1 and Broadcom GPIO-chip discovery, chip-select behavior, and the complete E673 command sequence.

Troubleshooting Canvas installation

If the package was installed without --build-from-source=canvas, the inky command may fail with an error like this on a 16 KB page-size Raspberry Pi kernel:

Error: libcairo.so.2: ELF load command address/offset not page-aligned

Remove the global package and reinstall it with the targeted source-build option:

sudo npm uninstall --global inky-impression-73
sudo npm install --global \
	--allow-scripts=canvas,exifreader,opengpio,spi-device \
	--build-from-source=canvas \
	inky-impression-73

You can confirm the kernel page size with getconf PAGESIZE. A result of 16384 means the system uses 16 KB pages.