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

@nickpaustian/spektra

v0.1.0

Published

An image pan and zoom viewer: an inline picture that expands to full screen and springs back, with drag, wheel, pinch and keys through helm, double-tap zoom and IIIF deep-zoom tiles

Downloads

146

Readme

@nickpaustian/spektra

An image that pans and zooms. Inline, a picture sits in the page and a click springs it from its box to full screen, where a drag or two fingers pan it (and fling it on release), the wheel, a pinch, a double tap and the keys zoom it toward the pointer, and Escape, a click beside it or a zoom out past the minimum spring it home. Expanded, it fills its element from the start. Pan is held in a rubber band that lets go while zooming in and tightens again after. A IIIF image service can lay sharper tiles over the picture as it is zoomed. Input is through helm, so a mouse, a trackpad, a touch screen and a pen all feel the same.

<figure id="plate"><img src="plate.jpg" alt="A plate" /></figure>
import { Spektra } from "@nickpaustian/spektra"

const viewer = new Spektra("#plate", {
  mode: "inline",
  padding: 40,
  maxScale: 8,
  iiif: "https://images.example/iiif/3/plate",
})

The element is replaced in the page by an anchor holding the same image, and the viewer floats above, hidden until it is expanded. The anchor takes the element's place in layout, so size the element as you would the picture. dispose() puts everything back.

Options

| option | default | | |---|---|---| | mode | expanded | inline expands on click; expanded fills its element | | expandOnClick | true | a click on the anchor expands | | fit | contain | contain, cover or none at zoom 0 | | padding | 0 | between the container's edge and the fitted image, a number or { top, right, bottom, left } | | boundsPadding | 0 | how far past the edge the image may be panned before the band pulls it back | | minScale, maxScale | 0.5 inline or 1 expanded, 5 | zoom limits as multiples of the fitted size; an in-place viewer does not shrink below its fit | | initialScale | the fit | opening zoom | | invertScroll | false | wheel down zooms in | | collapseOnZoomOut, collapseThreshold | true, −0.5 | zooming this many doublings below minScale collapses an inline viewer | | doubleTapZoom, doubleTapZoomLevel | true, 4 | a double tap springs to this scale, or back to the fit | | keyboard | true | arrows pan; +, - and shift with up or down zoom | | closeOnEscape | true | | | overlayBlur | 0 | pixels of backdrop blur behind the expanded viewer | | closeElement | a Close button | your own close control, used where it is | | iiif, iiifOptions | | a IIIF image service or manifest URL for tiles | | autoUpdate | true | step from its own frame loop; false and call update(dt) yourself | | createSignal | | make the signals with your own signal class | | feel | DEFAULT_FEEL | the springs and friction, all per second: panFriction, band, transitionZoom, transitionPan, flingWindow, overzoom, keyPanSpeed, keyZoomSpeed | | onExpandStart, onExpandEnd, onCollapseStart, onCollapseEnd | | callbacks; the same arrive as signals |

The feel

Every spring and friction is per second, so the motion is the same at 60 and 120 Hz. panFriction is the fraction of a fling's speed left after a second (0.00005 by default: a flick coasts for a tenth of a second or so). band is the spring that pulls the image back inside its bounds after a drag past the edge, and the zoom back inside its limits after an over-zoom; the zoom keeps no velocity between frames, so that pull is a plain ease with no overshoot. overzoom is how many doublings past a zoom limit the wheel or a pinch can push before the push fades to nothing: a quarter in place, and inline enough to reach the zoom-out collapse. transitionZoom and transitionPan are the springs of expand, collapse and zoomTo. Pass any of them in feel to retune; DEFAULT_FEEL holds the rest.

Methods and signals

  • expand(), collapse(), toggle(); Spektra.isOpen and Spektra.close() for whichever viewer is open; Spektra.get(el) and Spektra.destroy(el) by element or selector.
  • zoomTo(scale, aboutPointer = true) springs to a scale; togglePresetZoom() is what a double tap does.
  • setImageSource(src, iiif?) shows another picture; the anchor keeps the old one until the new has loaded. setTileSource(url, options?) and setTiles(source | null) change the tiles.
  • view is { x, y, zoom, scale }: the pan from centre in pixels, the zoom as a log2 exponent and as a multiple of the fit. mode and isTransitioning say where it is.
  • signals.onExpandStart, onExpandEnd, onCollapseStart, onCollapseEnd carry the viewer; onViewChange fires each frame the image moved.

Tiles

IIIFTileSource reads an Image API info.json (v2 or v3), or the first image service in a Presentation manifest, and once the image is zoomed past what its own pixels can show (tileThreshold, default 1.2 screen pixels per image pixel) lays tiles of the right level over it, keeping those already painted until the new level covers the view so a zoom never flashes blurry. TileSource is the interface: update(view) each frame and dispose(), for a pyramid of your own.

Styling

Styles are injected once per document. The classes to override: .spektra-anchor (the inline box), .spektra-expanded (the full-screen viewer, .spektra-dragging while a drag is on), .spektra-overlay (its background, --spektra-overlay for the colour, .spektra-active once faded in), .spektra-close (the default button) and .spektra-image (the moving box). The viewer, overlay and default button are children of body while expanded, so a focus trap takes those three.