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

@trippnology/imgwall

v1.0.1

Published

Infinite-scroll masonry image gallery server

Downloads

105

Readme

Image Wall

 __   __    __   ______   __     __   ______   __       __
/\ \ /\ "-./  \ /\  ___\ /\ \  _ \ \ /\  __ \ /\ \     /\ \
\ \ \\ \ \-./\ \\ \ \__ \\ \ \/ ".\ \\ \  __ \\ \ \____\ \ \____
 \ \_\\ \_\ \ \_\\ \_____\\ \__/".~\_\\ \_\ \_\\ \_____\\ \_____\
  \/_/ \/_/  \/_/ \/_____/ \/_/   \/_/ \/_/\/_/ \/_____/ \/_____/

Infinite-scroll masonry gallery server. Point it at a directory of images and it serves a web page with a masonry wall, thumbnails, and a lightbox — no build step, nothing written to disk.

Image Wall screenshot

Requirements

  • Node.js 18+

Install

npm install -g @trippnology/imgwall

Or from a checkout of this repo:

npm install -g .

Then run from anywhere:

imgwall [options]

Quick start

# Serve the current directory
imgwall

# Serve two directories, sorted by newest first, on port 8080
imgwall -d ~/pictures,~/wallpapers -s date-des -p 8080

The server picks a random free port, prints the URL, and opens your default browser at it.

CLI options

| Option | Description | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | -d, --directory <dirs> | Comma-separated directories to scan (default: current working directory) | | -e, --exclude-dirs <dirs> | Comma-separated subdirectory names to exclude (use with -r) | | -s, --sort <mode> | random (default), name-asc, name-des, date-asc, date-des | | -r, --recursive | Include subdirectories (default: top-level only) | | -a, --autoscroll [rate] | Auto-scroll the wall at rate px/sec (default 50); pauses on user input (5s idle resumes) and while the lightbox is open | | -p, --port <number> | Port (default: random free port) | | -l, --lan | Listen on all network interfaces — reachable from the local network (default: localhost only) | | --auth | Require a session token in the URL to view the wall (see below) | | --no-animation | Flatten animated images to static thumbnails | | --no-loop | Stop at the end of the image list instead of looping forever (default: loop) |

Auth

--auth requires a session token in the URL. A token is generated per run with crypto.randomBytes and printed in the startup URLs (e.g. http://localhost:1234/?auth=abc…). The first valid visit trades the ?auth= query for a 30-day HttpOnly cookie and is redirected to the clean URL; everything else gets a 401.

The token rotates every run, so bookmarked URLs from a previous run stop working. Run without --auth if that matters.

Scanning

  • Extensions: .jpg, .jpeg, .png, .gif, .webp (case-insensitive)
  • Date sort uses file mtime
  • File list + metadata (name, path, mtime) loaded once at startup; sort/shuffle applied once then held in memory — random order stays stable for the life of the server so infinite scroll doesn't skip or duplicate

Server

Built on hapi (ESM). Everything in memory; nothing written to disk.

Binds localhost by default; -l, --lan binds all interfaces (0.0.0.0) and startup prints LAN URLs too. --auth gates every route behind the token check (independent of --lan, timing-safe compare, token never echoed in responses).

Caching: a per-run version hash is appended to /img, /thumb, manifest, and icon URLs as ?v= — a restart reshuffles ids, so the version guarantees a stale cached thumb can never be served under a recycled id. Media responses get Cache-Control: public, max-age=86400 plus a strong ETag built from the source file's mtime and size (cheap 304 revalidation after expiry); icons and the manifest are immutable; the HTML shell and API are no-store.

Routes:

  • GET / — HTML shell (inline CSS/JS, no static files)
  • GET /manifest.webmanifest — PWA manifest (display: standalone) so Android "Add to Home screen" opens without browser chrome; icons are sharp-generated PNGs served from memory (/icon-192.png, /icon-512.png, incl. maskable)
  • GET /api/images?offset=&limit= — JSON batch of image metadata (batch size 25); client appends for infinite scroll. Unless disabled via --no-loop, the index wraps with modulo once the list is exhausted — same ids (and cache-hot thumbs) come round again, and the response's loop:true tells the client to keep scrolling forever
  • GET /img/:id — full-size image stream
  • GET /thumb/:id — thumbnail generated on demand via sharp (~400px wide, JPEG), cached in memory LRU (cap ~2000 entries)

Client

Inline in the HTML shell:

  • Masonry layout via JS column allocator (flex columns, append each item to the shortest column) — new items slot into the staggered gaps and existing items never move
  • Infinite scroll via IntersectionObserver sentinel → fetch next batch
  • Virtualized scrolling: anchors more than 2 viewports above the viewport are detached (height cached, per-column spacer preserves layout) and re-inserted on scroll up; passes are gated by scroll distance with per-frame caps so cost stays constant with depth
  • Lazy-load thumbnails (loading="lazy")
  • Media URLs carry the run's ?v= cache version (see Server)
  • Fancybox v5 (vanilla, no jQuery) lightbox: chevrons, arrow keys, mousewheel scroll through images; carousel built from the full loaded gallery, unaffected by virtualization trimming
  • On server start: auto-open default browser at served URL

Errors

  • Missing or invalid directory → clear CLI error, exit 1
  • Directory with zero images → error message, exit 1

Dependencies

commander, hapi, sharp, figlet, signale

License

MIT © Rikki Tripp - Trippnology