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

exifmat

v0.1.0

Published

Lightweight EXIF photo frame generator for browser and Node.js / Bun

Readme

exifmat

Lightweight EXIF photo frame generator for browser, Node.js, and Bun.

Draws photography frames with camera metadata on a Canvas 2D surface — no DOM layout, no SVG scene graph.

Install

bun add exifmat
# or: npm install exifmat

For server-side rendering, also install the optional canvas backend:

bun add @napi-rs/canvas

Default typeface is Inter Variable. In the browser, load it yourself (e.g. @fontsource-variable/inter). On Node/Bun, InterVariable.ttf is registered automatically via @napi-rs/canvas GlobalFonts when the canvas environment starts.

RAW support

Camera RAW files are supported without a full demosaic pipeline (keeps the library light and fast):

  • Formats: CR3, CR2, NEF, ARW, DNG, ORF, RW2, RAF, PEF, SRW, and other common RAW containers
  • Image: largest embedded JPEG preview is extracted and framed
  • Metadata: EXIF is read from the original RAW (with a fallback to the preview JPEG tags)
await createFrame({
  image: cr3Buffer,
  filename: "IMG_0001.CR3", // helpful when passing a bare buffer
  template: "gallery-white",
});

Quick start

import { createFrame, parseExif } from "exifmat";

const result = await createFrame({
  image: fileOrBuffer,
  template: "gallery-white",
  options: {
    showLogo: true,
    // Keep original brand colors (default: false = tint to text color)
    colorLogo: false,
    // When a logo is shown, hide the brand in the model label (default: true)
    // e.g. logo + "EOS R7" instead of logo + "Canon EOS R7"
    stripBrandFromModel: true,
    fields: ["camera", "lens", "focalLength", "aperture", "shutter", "iso"],
    // JPEG / WebP quality (0–1, default 0.92). Used by toBlob / toDataURL / toBuffer
    // when you omit the quality argument. Ignored for PNG.
    quality: 0.92,
    // Swap left/right caption groups on Gallery, Split, Minimal, Overlay, Sidebar
    // (default: 'ltr'). Centered templates (Stack, Overlay Center) ignore this.
    // captionDirection: 'rtl',
    // Optional: cap output width (scales down while preserving aspect ratio)
    // maxWidth: 1600,
  },
});

// Browser — quality defaults to options.quality
const blob = await result.toBlob("image/jpeg");

// Node / Bun
const buffer = await result.toBuffer("image/png");

Templates

| ID | Description | | ---------------------- | ----------------------------------------------------------------------- | | gallery-white | Full-bleed photo with white status bar (logo | model · settings) | | gallery-dark | Full-bleed photo with black status bar (logo | model · settings) | | gallery-white-lens | Gallery White with lens under logo/model | | gallery-dark-lens | Gallery Dark with lens under logo/model | | split-dark | Minimal-size mat: model/lens · logo | settings/date (dark) | | split-white | Minimal-size mat: model/lens · logo | settings/date (light) | | stack-white | Centered Overlay-style stack (logo → name → details) on a white bar | | stack-dark | Centered Overlay-style stack (logo → name → details) on a black bar | | stack-row-white | Logo + name on one row, details below (white bar) | | stack-row-dark | Logo + name on one row, details below (black bar) | | sidebar | White mat; right caption (logo → name → lens → settings) | | minimal | Museum mat with brand / exposure caption | | card | Rounded photo on a paper mat; centered camera + exposure caption | | crest-white | Logo above photo; camera + exposure below (light mat) | | crest-dark | Logo above photo; camera + exposure below (dark mat) | | spec-white | Brand header over a labelled focal / aperture / shutter / ISO grid | | spec-dark | Same grid on a dark mat | | rail-white | Caption set vertically in a slim column beside the photo | | rail-dark | Same column on a dark mat | | overlay | Full-bleed stacked caption on a soft gradient | | overlay-center | Full-bleed centered stack (logo → name → details) on a soft gradient | | overlay-gallery | Overlay caption in Gallery layout | | overlay-gallery-lens | Overlay caption in Gallery with lens layout | | overlay-rule | Gallery lens caption with a bottom hairline rule | | overlay-glass | Edge-color wash + blur glass strip; Gallery Lens caption; adaptive text | | overlay-glass-date | Top glass time/date; bottom glass Gallery Lens caption; adaptive text |

Register your own:

import { registerTemplate } from "exifmat";

registerTemplate({
  id: "my-frame",
  name: "My Frame",
  description: "Custom layout",
  async render(ctx) {
    // draw with ctx.ctx / ctx.image / ctx.exif
  },
});

Manufacturer logos

When showLogo is enabled, EXIF Make is matched to a bundled wordmark (Canon, Nikon, Sony, Fujifilm, Leica, Olympus / OM System, Panasonic, Apple, Google, Ricoh, Hasselblad, DJI).

To pin a specific logo instead of matching it from EXIF (for example when the make is missing or unrecognized), pass logoId with an id from listLogos():

import { createFrame, listLogos } from "exifmat";

console.log(listLogos().map((l) => l.id)); // ["alpa", "apple", ..., "sony", ...]

await createFrame({ image, options: { showLogo: true, logoId: "sony" } });

Unknown ids fall back to EXIF matching.

By default logos are tinted to the frame text color. Set colorLogo: true to keep each brand’s original colors (e.g. Canon red).

With a logo visible, stripBrandFromModel (default true) removes the brand from the camera line so you get logo + EOS R7 rather than logo + Canon EOS R7. Set it to false to keep the full model string.

License

MIT