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

photo-info

v0.4.1

Published

[![npm version](https://img.shields.io/npm/v/photo-info.svg?style=flat-square)](https://www.npmjs.com/package/photo-info) [![CI](https://github.com/tyom/photo-info/actions/workflows/ci.yaml/badge.svg)](https://github.com/tyom/photo-info/actions/workflows/

Readme

Photo Info

npm version CI

This package provides a function to extract useful information from photos, such as geo location, camera make and model, focal length and angle of view, which could be useful to orient the photo on the map.

Demo App

Try the demo app with a few geotagged photos. All photos operations are done in the browser. No data is sent to any server.

Installation

npm install photo-info

Usage

Basic Usage

import { getPhotoInfo } from 'photo-info';

const photoInfo = await getPhotoInfo(file);
console.log(photoInfo);
// {
//   make: 'Apple',
//   model: 'iPhone 15 Pro',
//   angleOfView: 23.5,
//   effectiveAngleOfView: 23.5,
//   bearing: 45.2,
//   gpsPosition: [51.5074, -0.1276, 10.5],
//   gpsAccuracy: {
//     error: 5,
//     grade: 'A',
//     description: 'Excellent - High confidence GPS fix'
//   },
//   gpsSpeed: { value: 5.4, unit: 'km/h' },
//   focalLength: 6.86,
//   focalLengthIn35mm: 48,
//   width: 4032,
//   height: 3024,
//   orientation: 'landscape',
//   frontCamera: false,
//   dateTime: '2024-03-15T14:30:00',
//   exposureTime: '1/120',
//   exposureProgram: 'Normal program',
//   fNumber: 'f/1.8',
//   lens: 'iPhone 15 Pro back camera 6.86mm f/1.78'
// }

Including Original EXIF Tags

const { originalTags, ...photoInfo } = await getPhotoInfo(file, true);

API Reference

getPhotoInfo(file: File, includeOriginalTags?: boolean): Promise<PhotoInfo>

Extracts photo information from an image file containing EXIF data.

Parameters

  • file - A File object (typically from an input element or drag-and-drop)
  • includeOriginalTags - Optional. When true, includes the raw EXIF data in the response

Returns

A PhotoInfo object with the following properties:

| Property | Type | Description | | ---------------------- | --------------------------------------- | ------------------------------------------------------------------- | | make | string \| null | Camera manufacturer (e.g., "Canon", "Apple") | | model | string \| null | Camera model (e.g., "iPhone 15 Pro") | | angleOfView | number \| null | Horizontal angle of view in degrees | | effectiveAngleOfView | number \| null | Effective FOV for map display (considers orientation) | | bearing | number \| null | Compass direction the camera was facing (0-360°) | | gpsPosition | [lat, lng, alt?] \| null | GPS coordinates: latitude, longitude, altitude (meters) | | gpsAccuracy | {error, grade, description} \| null | GPS accuracy with error (meters), grade (A-F), and text description | | gpsSpeed | {value, unit} \| null | Speed at capture time with unit (typically "km/h") | | focalLength | number \| null | Actual focal length in millimeters | | focalLengthIn35mm | number \| null | 35mm equivalent focal length | | width | number | Image width in pixels | | height | number | Image height in pixels | | orientation | 'portrait' \| 'landscape' \| 'square' | Image orientation | | frontCamera | boolean | Whether taken with front-facing camera | | dateTime | string \| null | ISO 8601 formatted capture time | | exposureTime | string \| null | Shutter speed (e.g., "1/120") | | exposureProgram | string \| null | Camera exposure mode | | fNumber | string \| null | Aperture value (e.g., "f/1.8") | | lens | string \| null | Lens model/description | | originalTags | object \| undefined | Raw EXIF data (when requested) |

getMappedPhotoInfo(file: File): Promise<MappedExifData>

Get photo information with user-friendly mapped EXIF data.

Returns

A mapped object where each EXIF tag has:

  • value - The raw EXIF value
  • displayName - Human-readable property name
  • formattedValue - Formatted value for display
const mappedData = await getMappedPhotoInfo(file);
console.log(mappedData.ISO);
// {
//   value: 100,
//   displayName: 'ISO Speed',
//   formattedValue: 'ISO 100'
// }

getGroupedPhotoInfo(file: File): Promise<GroupedExifData>

Get photo EXIF data organized by categories.

Returns

EXIF data grouped into categories:

  • Camera - Make, model, lens info
  • GPS - Location, altitude, speed
  • Image - Dimensions, orientation, compression
  • Capture - Date, time, settings
  • Other - Additional metadata
const grouped = await getGroupedPhotoInfo(file);
console.log(grouped.Camera);
// { Make: 'Canon', Model: 'EOS R5', ... }
console.log(grouped.GPS);
// { GPSLatitude: 51.5074, GPSLongitude: -0.1276, ... }

getComprehensivePhotoInfo(file: File): Promise<...>

Get all photo information formats in a single call.

Returns

An object containing:

  • original - Standard PhotoInfo with all fields
  • mapped - User-friendly mapped EXIF data
  • grouped - EXIF data organized by categories
const comprehensive = await getComprehensivePhotoInfo(file);
console.log(comprehensive.original); // PhotoInfo object
console.log(comprehensive.mapped); // MappedExifData
console.log(comprehensive.grouped); // GroupedExifData

createFovMarkerSvg(options: MarkerOptions): string

Creates an SVG string for visualizing photo field-of-view on maps.

import { createFovMarkerSvg } from 'photo-info';

const svgString = createFovMarkerSvg({
  angleOfView: 65,
  bearing: 180,
  circleColor: 'red',
  fovColor: 'rgba(255, 0, 0, 0.3)',
});

// Use with Leaflet, Mapbox, or other mapping libraries

Options

  • angleOfView - Field of view angle in degrees
  • bearing - Direction in degrees (0-360)
  • viewBoxSize - SVG viewbox size (default: 200)
  • circleSize - Center marker size (default: 5)
  • circleColor - Center marker color (default: 'orange')
  • fovColor - Field of view wedge color (default: 'lightblue')

Error Handling

The library gracefully handles missing or invalid EXIF data:

try {
  const info = await getPhotoInfo(file);

  // Properties will be null when data is unavailable
  if (info.gpsPosition) {
    console.log('Photo has GPS coordinates');
  }

  // Core properties like width/height default to 0 if unavailable
  if (info.width === 0) {
    console.log('Could not determine image dimensions');
  }
} catch (error) {
  // File reading errors will throw
  console.error('Failed to read file:', error);
}

Utility Functions

The library also exports various utility functions for advanced use:

Camera & Lens Calculations

import {
  calculateAngleOfView,
  calculateAnglesOfView,
  calculateSensorSize,
  calculate35mmEquivalentFocalLength,
  calculateCropFactor,
} from 'photo-info';

// Calculate field of view angles
const { horizontal, vertical } = calculateAnglesOfView(
  24, // focal length in mm
  50, // 35mm equivalent
  '3:2', // aspect ratio
);

// Calculate sensor dimensions
const sensorSize = calculateSensorSize(5.6, '4:3'); // crop factor and aspect ratio

// Calculate 35mm equivalent focal length
const equiv = calculate35mmEquivalentFocalLength(24, 1.5); // focal length and crop factor

Practical Examples

Display photos on a map with GPS accuracy

const photos = await Promise.all(
  files.map(async (file) => ({
    file,
    info: await getPhotoInfo(file),
  })),
);

// Filter photos with GPS data
const geotaggedPhotos = photos.filter((p) => p.info.gpsPosition);

// Add markers to your map
geotaggedPhotos.forEach(({ info }) => {
  const [lat, lng] = info.gpsPosition;

  // Create marker with field-of-view indicator
  const marker = L.marker([lat, lng]);

  // Show GPS accuracy if available
  if (info.gpsAccuracy) {
    const { error, grade, description } = info.gpsAccuracy;
    marker.bindPopup(`
      GPS Accuracy: ${description}
      Error: ±${error}m (Grade ${grade})
    `);
  }

  if (info.effectiveAngleOfView && info.bearing) {
    // Use effectiveAngleOfView for correct orientation handling
    const svg = createFovMarkerSvg({
      angleOfView: info.effectiveAngleOfView,
      bearing: info.bearing,
    });
    // Add SVG overlay to map
  }
});

Extract camera settings

const info = await getPhotoInfo(file);

if (info.fNumber && info.exposureTime && info.focalLength) {
  console.log(
    `Shot at ${info.fNumber}, ${info.exposureTime}s, ${info.focalLength}mm`,
  );

  if (info.focalLengthIn35mm) {
    console.log(`35mm equivalent: ${info.focalLengthIn35mm}mm`);
  }
}

Working with different data formats

// Get comprehensive data in one call
const { original, mapped, grouped } = await getComprehensivePhotoInfo(file);

// Access camera info from grouped data
console.log('Camera:', grouped.Camera);

// Get user-friendly display values
for (const [tag, data] of Object.entries(mapped)) {
  console.log(`${data.displayName}: ${data.formattedValue}`);
}

Browser Compatibility

This library requires:

  • Modern browser with File API support
  • async/await support (or transpilation)
  • Works with JPEG, TIFF, PNG, HEIC, and WebP files containing EXIF data