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

exiftool-vendored

v35.9.0

Published

Efficient, cross-platform access to ExifTool

Readme

exiftool-vendored

Fast, cross-platform Node.js access to ExifTool. Built and supported by PhotoStructure.

npm version Node.js CI GitHub issues

Installation & Quick Start

Requirements: Node.js Active LTS or Maintenance LTS versions only

npm install exiftool-vendored
import { exiftool } from "exiftool-vendored";

// Read metadata
const tags = await exiftool.read("photo.jpg");
console.log(`Camera: ${tags.Make} ${tags.Model}`);
console.log(`Taken: ${tags.DateTimeOriginal}`);
console.log(`Size: ${tags.ImageWidth}x${tags.ImageHeight}`);

// Write metadata
await exiftool.write("photo.jpg", {
  XPComment: "Amazing sunset!",
  Copyright: "© 2024 Your Name",
});

// Extract thumbnail
await exiftool.extractThumbnail("photo.jpg", "thumb.jpg");

await exiftool.end();

Why exiftool-vendored?

Performance

Order of magnitude faster than other Node.js ExifTool modules. Powers PhotoStructure and 1,000+ other projects.

🔧 Battle-tested

  • Cross-platform: macOS, Linux, Windows
  • Full-featured: Read, write, extract embedded images
  • Reliable: Extensive test coverage across most camera manufacturers

📚 Developer-Friendly

  • TypeScript: Full type definitions for thousands of metadata fields
  • Smart dates: Timezone-aware ExifDateTime classes
  • Auto-generated tags: Based on 10,000+ real camera samples

Core Features

Reading Metadata

const tags = await exiftool.read("photo.jpg");

// Camera info
console.log(tags.Make, tags.Model, tags.LensModel);

// Capture settings
console.log(tags.ISO, tags.FNumber, tags.ExposureTime);

// Location (if available)
console.log(tags.GPSLatitude, tags.GPSLongitude);

// Always check for parsing errors
if (tags.errors?.length > 0) {
  console.warn("Metadata warnings:", tags.errors);
}

Writing Metadata

// Add keywords and copyright
await exiftool.write("photo.jpg", {
  Keywords: ["sunset", "landscape"],
  Copyright: "© 2024 Photographer Name",
  "IPTC:CopyrightNotice": "© 2024 Photographer Name",
});

// Update all date fields at once
await exiftool.write("photo.jpg", {
  AllDates: "2024:03:15 14:30:00",
});

// Delete tags
await exiftool.write("photo.jpg", {
  UserComment: null,
});

Extracting Images

// Extract thumbnail
await exiftool.extractThumbnail("photo.jpg", "thumbnail.jpg");

// Extract preview (larger than thumbnail)
await exiftool.extractPreview("photo.jpg", "preview.jpg");

// Extract JPEG from RAW files
await exiftool.extractJpgFromRaw("photo.cr2", "processed.jpg");

Understanding Tags

The Tags interface contains thousands of metadata fields from an auto-generated TypeScript file. Each tag has JSDoc annotations:

/**
 * @frequency 🔥 ★★★★ (85%)
 * @groups EXIF, MakerNotes
 * @example 100
 */
ISO?: number;

/**
 * @frequency 🧊 ★★★☆ (23%)
 * @groups MakerNotes
 * @example "Custom lens data"
 */
LensSpec?: string;
  • 🔥 = Found on mainstream devices (iPhone, Canon, Nikon, Sony)
  • 🧊 = Only found on more obscure camera makes and models
  • ★★★★ = Found in >50% of files, ☆☆☆☆ = rare (<1%)
  • @groups = Metadata categories (EXIF, GPS, IPTC, XMP, etc.)
  • @example = Representative values

Code defensively!

  • No fields are guaranteed to be present.
  • Value types are not guaranteed -- assume strings may get in your numeric fields, and handle it gracefully.
  • There may very well be keys returned that are not in the Tags interface.

📖 Complete Tags Documentation →

Important Notes

⚙️ Configuration

exiftool-vendored provides two levels of configuration:

Library-wide Settings - Global configuration affecting all instances:

import { Settings } from "exiftool-vendored";

// Enable parsing of archaic timezone offsets for historical photos
Settings.allowArchaicTimezoneOffsets.value = true;

Per-instance Options - Configuration for individual ExifTool instances:

import { ExifTool } from "exiftool-vendored";

const exiftool = new ExifTool({
  maxProcs: 8, // More concurrent processes
  useMWG: true, // Use Metadata Working Group tags
  backfillTimezones: true, // Infer missing timezones
});

📖 Complete Configuration Guide →

⏰ Dates & Timezones

Images rarely specify timezones. This library infers them using several heuristics:

  1. Explicit metadata (TimeZoneOffset, OffsetTime)
  2. GPS location → timezone lookup
  3. UTC timestamps → calculate offset
const dt = tags.DateTimeOriginal;
if (dt instanceof ExifDateTime) {
  console.log("Timezone offset:", dt.tzoffset, "minutes");
  console.log("Timezone:", dt.zone);
}

📖 Date & Timezone Guide →

🧹 Resource Cleanup

As of v35, Node.js will exit naturally without calling .end() — child processes are cleaned up automatically when the parent exits.

For long-running applications (servers, daemons), calling .end() is still recommended for graceful shutdown:

import { exiftool } from "exiftool-vendored";

// For servers/daemons: graceful shutdown on termination signals
process.on("SIGINT", () => exiftool.end());
process.on("SIGTERM", () => exiftool.end());

Automatic Cleanup with Disposable Interfaces

For TypeScript 5.2+ projects, consider using automatic resource management:

import { ExifTool } from "exiftool-vendored";

// Automatic synchronous cleanup
{
  using et = new ExifTool();
  const tags = await et.read("photo.jpg");
  // ExifTool automatically cleaned up when block exits
}

// Automatic asynchronous cleanup (recommended)
{
  await using et = new ExifTool();
  const tags = await et.read("photo.jpg");
  // ExifTool gracefully cleaned up when block exits
}

Benefits:

  • Guaranteed cleanup: No leaked processes, even with exceptions
  • Timeout protection: Automatic forceful cleanup if graceful shutdown hangs
  • Zero boilerplate: No manual .end() calls needed

Caution:

  • Operating-system startup lag: Linux costs ~50-500ms to launch a new ExifTool process, but macOS can take several seconds (presumably due to Gatekeeper), and Windows can take tens of seconds due to antivirus shenanigans. Don't dispose your instance unless you're really done with it!

🏷️ Tag Completeness

The Tags interface shows the most common fields, but ExifTool can extract many more. Cast to access unlisted fields:

const tags = await exiftool.read("photo.jpg");
const customField = (tags as any).UncommonTag;

Documentation

📚 Guides

🔧 Troubleshooting

📖 API Reference

Performance

The default singleton is throttled for stability. For high-throughput processing:

import { ExifTool } from "exiftool-vendored";

const exiftool = new ExifTool({
  maxProcs: 8, // More concurrent processes
  minDelayBetweenSpawnMillis: 0, // Faster spawning
  streamFlushMillis: 10, // Faster streaming
});

// Process many files efficiently
const results = await Promise.all(filePaths.map((file) => exiftool.read(file)));

await exiftool.end();

Benchmarks: 20+ files/second/thread, 500+ files/second using all CPU cores.

Support & Community

Contributors 🎉

Matthew McEachen, Joshua Harris, Anton Mokrushin, Luca Ban, Demiurga, David Randler