πŸ“· The fastest and most versatile JS EXIF reading library.

Overview

exifr

Build Status Coverage Status gzip size Dependency Status jsDelivr downloads npm downloads size NPM Version

Usage β€’ Installation β€’ Quick start β€’ Demos β€’ API β€’ Perf β€’ Changelog β€’ FAQ β€’ Contributing

πŸ“· The fastest and most versatile JavaScript EXIF reading library.

Try it yourself - demo page & playground.

Features

Works everywhere, parses anything you throw at it.

  • 🏎️ Fastest EXIF lib: +-1ms per file
  • πŸ—ƒοΈ Any input: buffers, url, <img> tag, and more
  • πŸ“· Files: .jpg, .tif, .heic, .png, .iiq
  • πŸ”Ž Segments: TIFF (EXIF, GPS, etc...), XMP, ICC, IPTC, JFIF, IHDR
  • πŸ“‘ Reads only first few bytes
  • πŸ”¬ Skips parsing tags you don't need
  • ✨ Isomorphic: Browser & Node.js
  • πŸ—œοΈ No dependencies
  • πŸ–ΌοΈ Extracts thumbnail
  • πŸ’” Salvages broken files
  • 🧩 Modular
  • πŸ“š Customizable tag dictionaries
  • πŸ“¦ Bundled as UMD/CJS or ESM
  • βœ” Tested and benchmarked
  • πŸ€™ Promises
  • πŸ•Έ Supports even IE11 IE10
and more (click to expand)
  • XMP Parser - minimalistic, reliable, without dependencies
  • XMP Extended
  • Multi-segment ICC
  • Extracts all ICC tags (RedMatrixColumn, GreenTRC, B2A2, etc...)
  • TIFF dictionaries contain less frequently used, non-standard and proprietary TIFF/EXIF tags (only in full bundle)
  • Handles UCS2 formatted strings (XPTitle tag), instead of leaving it as a buffer
  • Normalizes strings
  • Revives dates into Date class instances
  • Converts GPS coords from DMS to DD format. From `GPSLatitude, GPSLatitudeRef tags ([50, 17, 58.57] & "N") to single latitude value (50.29960).
  • Instructs how to rotate photo with exifr.rotation() and accounts for quirky autorotation behavior of iOs Safari and Chrome 81 and newer

You don't need to read the whole file to tell if there's EXIF in it. And you don't need to extract all the data when you're looking for just a few tags. Exifr just jumps through the file structure, from pointer to pointer. Instead of reading it byte by byte, from beginning to end.

Exifr does what no other JS lib does. It's efficient and blazing fast!

Segments JPEG TIFF / IIQ HEIC PNG
EXIF/TIFF, GPS βœ” βœ” βœ” βœ”
XMP βœ” βœ” ❌ βœ”
IPTC βœ” βœ” ❌ 🟑 (If it's a part of IHDR)
ICC βœ” βœ” βœ” βœ” (Node.js only, requires zlib)
Thumbnail βœ” ❌ ❌ ❌
JFIF (JPEG header) βœ” ⚫ ⚫ ⚫
IHDR (PNG header) ⚫ ⚫ ⚫ βœ”

Usage

file can be any binary format (Buffer, Uint8Array, Blob and more), <img> element, string path or url.

options specify what segments and blocks to parse, filters what tags to pick or skip.

API Returns Description
exifr.parse(file) object Parses IFD0, EXIF, GPS blocks
exifr.parse(file, true) object Parses everything
exifr.parse(file, ['Model', 'FNumber', ...]) object Parses only specified tags
exifr.parse(file, {options}) object Custom settings
exifr.gps(file) {latitude, longitude} Parses only GPS coords
exifr.orientation(file) number Parses only orientation
exifr.rotation(file) object Returns how to rotate the photo
exifr.thumbnail(file) Buffer|Uint8Array binary Extracts embedded thumbnail
exifr.thumbnailUrl(file) string Object URL Browser only

Installation

npm install exifr

Exifr comes in three prebuilt bundles. It's a good idea to start development with full and then scale down to lite, mini, or better yet, build your own around modular core.

// Modern Node.js can import CommonJS
import exifr from 'exifr' // => exifr/dist/full.umd.cjs
// Explicily import ES Module
import exifr from 'exifr/dist/full.esm.mjs' // to use ES Modules
// CommonJS, old Node.js
var exifr = require('exifr') // => exifr/dist/full.umd.cjs
<!-- ES Module in modern browsers -->
<script type="module">import exifr from 'node_modules/exifr/dist/lite.esm.js';</script>
<!-- classic UMD script -->
<script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.umd.js"></script>
<!-- IE10 & old browsers. You also need Promise polyfill -->
<script src="https://cdn.jsdelivr.net/npm/exifr/dist/lite.legacy.umd.js"></script>

Browsers: lite and mini are recommended because of balance between features and file size. UMD format attaches the library to global window.exifr object.

IE & old browsers: legacy builds come bundled with polyfills. Learn more.

Bundles & formats

  • full - Contains everything. Intended for use in Node.js.
  • lite - Reads JPEG and HEIC. Parses TIFF/EXIF and XMP.
  • mini - Stripped down to basics. Parses most useful TIFF/EXIF from JPEGs. Has no tag dictionaries.

Of course, you can use the full version in browser, or use any other build in Node.js.

  • ESM - Modern syntax for use in modern browsers and Node.js.
    Uses import syntax.
  • UMD - Universal format for browsers and Node.js.
    Supports CJS require('exifr'), AMD/RequireJS and global window.exifr.
  • legacy UMD - For use in older browsers (up to IE10).
    Bundled with polyfills & shims, except for Promise polyfill. Learn more here.
Detailed comparison (click to expand)
full lite mini core
chunked
file readers
BlobReader
UrlFetcher
FsReader
Base64Reader
BlobReader
UrlFetcher
BlobReader none
file parsers *.jpg
*.heic
*.tif/*.iiq
*.png
*.jpg
*.heic
*.jpg none
segment
parsers
TIFF (EXIF)
IPTC
XMP
ICC
JFIF
IHDR
TIFF (EXIF)
XMP
TIFF (EXIF) none
dictionaries TIFF (+ less frequent tags)
IPTC
ICC
JFIF
IHDR
only TIFF keys
(IFD0, EXIF, GPS)
none none
size +- 60 Kb 40 Kb 25 Kb 15 Kb
gzipped 22 Kb 12 Kb 8 Kb 4 Kb
file full.umd.js
full.esm.js
full.esm.mjs
full.legacy.umd.js
lite.umd.js
lite.esm.js
lite.esm.mjs
lite.legacy.umd.js
mini.umd.js
mini.esm.js
mini.esm.mjs
mini.legacy.umd.js
Learn more

ESM, .js .mjs .cjs extensions, "main", "module", "type":"module"

TL;DR: All bundles are available in two identical copies. .mjs and .js for ESM. .cjs and .js for UMD. Pick one that works with your tooling or webserver.

(click to expand for more info)

Current state of ESM is complicated. Node.js can already handle ESM files with .mjs extension and modules with "type":"module" in package.json. Turns out the "type":"module" approach alone is not yet ready for production. Some bundlers and tools may work or break with .mjs extension, whereas it's important for Node.js. The same applies to the new .cjs extension (introduced in Node.js 13).

The library is written in ESM, with .mjs extensions and transpiled to both ESM and UMD formats.

The "main" (field in package.json) entry point is now full.umd.cjs but you can still use ESM by explicitly importing full.esm.mjs. "module" field (used by some tools) points to full.esm.mjs.

If your webserver isn't configured to handle .mjs or .cjs files you can use their identical .js clone. For example full.esm.mjs is identical to full.esm.js. So is lite.esm.cjs to lite.esm.js. Just pick one that fits your tools or environment.

Named exports vs default export

Exifr exports both named exports and a default export - object containing all the named exports.

You can use import * as exifr from 'exifr' as well as import exifr from 'exifr' (recommended).

Examples

// exifr reads the file from disk, only a few hundred bytes.
exifr.parse('./myimage.jpg')
  .then(output => console.log('Camera:', output.Make, output.Model))
// Or read the file on your own and feed the buffer into exifr.
fs.readFile('./myimage.jpg')
  .then(exifr.parse)
  .then(output => console.log('Camera:', output.Make, output.Model))

Extract only certain tags

// only GPS
let {latitude, longitude} = await exifr.gps('./myimage.jpg')
// only orientation
let num = await exifr.orientation(blob)
// only three tags
let output = await exifr.parse(file, ['ISO', 'Orientation', 'LensModel'])
// only XMP segment (and disabled TIFF which is enabled by default)
let output = await exifr.parse(file, {tiff: false, xmp: true})

Extracting thumbnail

let thumbBuffer = await exifr.thumbnail(file)
// or get object URL (browser only)
img.src = await exifr.thumbnailUrl(file)

Web Worker

let worker = new Worker('./worker.js')
worker.postMessage('../test/IMG_20180725_163423.jpg')
worker.onmessage = e => console.log(e.data)
// tip: try Transferable Objects with ArrayBuffer
worker.postMessage(arrayBuffer, [arrayBuffer])
// worker.js
importScripts('./node_modules/exifr/dist/lite.umd.js')
self.onmessage = async e => postMessage(await exifr.parse(e.data))

UMD in Browser

<img src="./myimage.jpg">
<script src="./node_modules/exifr/dist/lite.umd.js"></script>
<script>
  let img = document.querySelector('img')
  window.exifr.parse(img).then(exif => console.log('Exposure:', exif.ExposureTime))
</script>

ESM in Browser

<input id="filepicker" type="file" multiple>
<script type="module">
  import exifr from './node_modules/exifr/dist/lite.esm.js'
  document.querySelector('#filepicker').addEventListener('change', async e => {
    let files = Array.from(e.target.files)
    let exifs = await Promise.all(files.map(exifr.parse))
    let dates = exifs.map(exif => exif.DateTimeOriginal.toGMTString())
    console.log(`${files.length} photos taken on:`, dates)
  })
</script>

Demos

and a lot more in the examples/ folder

API

parse(file[, options])

Returns: Promise<object>

Accepts file (in any format), parses it and returns exif object. Optional options argument can be specified.

gps(file)

Returns: Promise<object>

Only extracts GPS coordinates.

Uses pick/skip filters and perf improvements to only extract latitude and longitude tags from GPS block. And to get GPS-IFD pointer it only scans through IFD0 without reading any other unrelated data.

Check out examples/gps.js to learn more.

orientation(file)

Returns: Promise<number>

Only extracts photo's orientation.

rotation(file)

Returns: Promise<object>

Only extracts photo's orientation. Returns object with instructions how to rotate the image:

  • deg <number>: angle in degrees (i.e. 180), useful for css transform: rotate()
  • rad <number>: angle in radians (i.e. 3.141592653589793) useful for canvas' ctx.rotate()
  • scaleX <number>: image is (-1) or isn't (1) mirrored horizontally
  • scaleY <number>: image is (-1) or isn't (1) mirrored upside down
  • dimensionSwapped <boolean>: image is rotated by 90Β° or 270Β°. Fixing rotation would swap width and height.
  • css <boolean>: can/can't be rotated with CSS and transform: rotate() (important for ios Safari)
  • canvas <boolean>: can/can't be rotated with canvas and ctx.rotate() (important for ios Safari)

Warning: Some modern browsers autorotate <img> elements, background-image and/or data passed to <canvas> without altering the EXIF. The behavior is extra quirky on iOs 13.4 Safari and newer (though not on macos). You may end up with over-rotated image if you don't handle this quirk. See examples/orientation.html to learn more.

let r = await exifr.rotation(image)
if (r.css) {
  img.style.transform = `rotate(${r.deg}deg) scale(${r.scaleX}, ${r.scaleY})`
}

thumbnail(file)

Returns: Promise<Buffer|Uint8Array>

Extracts embedded thumbnail from the photo, returns Uint8Array.

Only parses as little EXIF as necessary to find offset of the thumbnail.

Check out examples/thumbnail.html and examples/thumbnail.js to learn more.

thumbnailUrl(file)

Returns: Promise<string>
browser only

Exports the thumbnail wrapped in Object URL. The URL has to be revoked when not needed anymore.

Exifr class

Aforementioned functions are wrappers that internally:

  1. instantiate new Exifr(options) class
  2. call .read(file) to load the file
  3. call .parse() or .extractThumbnail() to get an output

You can instantiate Exif yourself to parse metadata and extract thumbnail efficiently at the same time. In Node.js it's also necessary to close the file with .file.close() if it's read in the chunked mode.

let exr = new Exifr(options)
await exr.read(file)
let output = await exr.parse()
let buffer = await exr.extractThumbnail()
await exr.file?.close?.()

file argument

  • string
  • Buffer
  • ArrayBuffer
  • Uint8Array
  • DataView
  • Blob, File
  • <img> element

options argument

All other and undefined properties are inherited from defaults:

let defaultOptions = {
  // Segments (JPEG APP Segment, PNG Chunks, HEIC Boxes, etc...)
  tiff: true,
  xmp: false,
  icc: false,
  iptc: false,
  jfif: false, // (jpeg only)
  ihdr: false, // (png only)
  // Sub-blocks inside TIFF segment
  ifd0: true, // aka image
  ifd1: false, // aka thumbnail
  exif: true,
  gps: true,
  interop: false,
  // Other TIFF tags
  makerNote: false,
  userComment: false,
  // Filters
  skip: [],
  pick: [],
  // Formatters
  translateKeys: true,
  translateValues: true,
  reviveValues: true,
  sanitize: true,
  mergeOutput: true,
  silentErrors: true,
  // Chunked reader
  chunked: true,
  firstChunkSize: undefined,
  firstChunkSizeNode: 512,
  firstChunkSizeBrowser: 65536, // 64kb
  chunkSize: 65536, // 64kb
  chunkLimit: 5
}

Tag filters

Exifr can avoid reading certain tags, instead of reading but not including them in the output, like other exif libs do. For example MakerNote tag from EXIF block is isually very large - tens of KBs. Reading such tag is a waste of time if you don't need it.

Tip: Using numeric tag codes is even faster than string names because exifr doesn't have to look up the strings in dictionaries.

options.pick

Type: Array<string|number>

Array of the only tags that will be parsed.

Specified tags are looked up in a dictionary. Their respective blocks are enabled for parsing, all other blocks are disabled. Parsing ends as soon as all requested tags are extracted.

// Only extracts three tags from EXIF block. IFD0, GPS and other blocks disabled.
{pick: ['ExposureTime', 'FNumber', 'ISO']}
// Only extracts three tags from EXIF block and one tag from GPS block.
{pick: ['ExposureTime', 'FNumber', 'ISO', 'GPSLatitude']}
// Extracts two tags from GPS block and all of IFD0 and EXIF blocks which are enabled by default.
{gps: {pick: ['GPSLatitude', 0x0004]}}

options.skip

Type: Array<string|number>
Default: ['MakerNote', 'UserComments']

Array of the tags that will not be parsed.

By default, MakerNote and UserComment tags are skipped. But that is configured elsewhere.

// Skips reading these three tags in any block
{skip: ['ImageWidth', 'Model', 'FNumber', 'GPSLatitude']}
// Skips reading three tags in EXIF block
{exif: {skip: ['ImageUniqueID', 42033, 'SubSecTimeDigitized']}}

Segments & Blocks

EXIF became synonymous for all image metadata, but it's actually just one of many blocks inside TIFF segment. And there are more segment than just TIFF.

Segments (JPEG APP Segments, HEIC Boxes, PNG Chunks)

Jpeg stores various formats of data in APP-Segments. Heic and Tiff file formats use different structures or naming conventions but the idea is the same, so we refer to TIFF, XMP, IPTC, ICC and JFIF as Segments.

  • options.tiff type bool|object|Array default: true
    TIFF APP1 Segment - Basic TIFF/EXIF tags, consists of blocks: IFD0 (image), IFD1 (thumbnail), EXIF, GPS, Interop
  • options.jfif type bool default: false
    JFIF APP0 Segment - Additional info
  • options.xmp type bool default: false
    XMP APP1 Segment - additional XML data
  • options.iptc type bool default: false
    IPTC APP13 Segment - Captions and copyrights
  • options.icc type bool default: false
    ICC APP2 Segment - Color profile
  • options.ihdr type bool default: true (only for PNG)
    PNG Header chunk - Basic file info

TIFF IFD Blocks

TIFF Segment consists of various IFD's (Image File Directories) aka blocks.

  • options.ifd0 (alias options.image) type bool|object|Array default: true
    IFD0 - Basic info about the image
  • options.ifd1 (alias options.thumbnail) type bool|object|Array default: false
    IFD1 - Info about embedded thumbnail
  • options.exif type bool|object|Array default: true
    EXIF SubIFD - Detailed info about photo
  • options.gps type bool|object|Array default: true
    GPS SubIFD - GPS coordinates
  • options.interop type bool|object|Array default: false
    Interop SubIFD - Interoperability info

Notable TIFF tags

Notable large tags from EXIF block that are not parsed by default but can be enabed if needed.

  • options.makerNote type: bool default: false
    0x927C MakerNote tag
  • options.userComment type: bool default: false
    0x9286 UserComment tag

XMP

Extracted XMP tags are grouped by namespace. Each ns is separate object in output. E.g. output.xmlns, output.GPano, output.crs, etc...

For XMP Extended see options.multiSegment

Exifr contains minimalistic opinionated XML parser for parsing data from XMP. It may not be 100% spec-compliant, because XMP is based on XML which cannot be translated 1:1 to JSON. The output is opinionated and may alter or simplify the data structure. If the XMP parser doesn't suit you, it can be disabled by setting options.xmp.parse to false. Then a raw XMP string will be available at output.xmp.

Caveats & XML to JSON mapping
  1. Tags with both attributes and children-value are combined into object.
  2. Arrays (RDF Containers) with single item are unwrapped. The single item is used in place of the array.
  3. If options.mergeOutput:false: Tags of tiff namespace (<tiff:Model>) are merged into output.ifd0. Likewise exif ns is merged into output.exif.
<rdf:Description foo:name="Exifr">
  <foo:author>Mike KovaΕ™Γ­k</foo:author>
  <foo:description xml:lang="en-us">Some string here</foo:description>
  <foo:formats><rdf:Seq><rdf:li>jpeg</rdf:li></rdf:Seq></foo:formats>
  <foo:segments><rdf:Seq><rdf:li>xmp</rdf:li><rdf:li>tiff</rdf:li><rdf:li>iptc</rdf:li></rdf:Seq></foo:segments>
</rdf:Description>

parses as:

{
  name: 'Exifr', // attribute belonging to the same namespace
  author: 'Mike KovaΕ™Γ­k', // simple tag of the namespace
  description: {lang: 'en-us', value: 'Some string here'}, // tag with attrs and value becomes object
  formats: 'jpeg', // single item array is unwrapped
  segments: ['xmp', 'tiff', 'iptc'] // array as usual
}

options.multiSegment

Type: bool
Default: false

Enables looking for more than just a single segment of ICC or XMP (XMP Extended).

In some rare cases the photo can contain additional layers, embedded images, or metadata that doesn't fit inside single 64kB (JPEG) segment.

Side effect: Disables chunked reading. The whole file has to be read to locate all segments.

When is it useful:

  • VR photos with combination of left/right eye (XMP Extended)
  • "Portrait mode" photo that contains depth map (XMP Extended)
  • Photos with custom ICC color profile

Sub-options:

  • options.xmp.multiSegment
  • options.icc.multiSegment

Shortcuts

options.tiff serves as a shortcut for configuring all TIFF blocks:

  • options.tiff = true enables all TIFF blocks (sets them to true).
  • options.tiff = false disables all TIFF blocks (sets them to false) except for those explicitly set to true in options.
  • options.tiff = {...} applies the same sub-options to all TIFF blocks that are enabled.

options.tiff = false can be paired with any other block(s) to disable all other blocks except for said block.

{interop: true, tiff: false}
// is a shortcut for
{interop: true, ifd0: false, exif: false, gps: false, ifd1: true}

Each TIFF block and the whole tiff segment can also be configured with object or array, much like the options argument.

TIFF blocks automatically inherit from options.tiff and then from options.

// Only extract FNumber + ISO tags from EXIF and GPSLatitude + GPSLongitude from GPS
{
  exif: true, gps: true,
  pick: ['FNumber', 'ISO', 'GPSLatitude', 0x0004] // 0x0004 is GPSLongitude
}
// is a shortcut for
{exif: ['FNumber', 'ISO'], gps: ['GPSLatitude', 0x0004]}
// which is another shortcut for
{exif: {pick: ['FNumber', 'ISO']}, gps: {pick: ['GPSLatitude', 0x0004]}}

Chunked reader

options.chunked

Type: bool
Default: true

Exifr can read only a few chunks instead of the whole file. It's much faster, saves memory and unnecessary disk reads or network fetches. Works great with complicated file structures - .tif files may point to metadata scattered throughout the file.

How it works: A first small chunk (of firstChunkSize) is read to determine if the file contains any metadata at all. If so, reading subsequent chunks (of chunkSize) continues until all requested segments are found or until chunkLimit is reached.

Supported inputs: Chunked is only effective with Blob, <img> element, string url, disk path, or base64. These inputs are not yet processed or read into memory. Each input format is implemented in a separate file reader class. Learn more about file readers and modularity here.

If you use URL as input: Fetching chunks (implemented in UrlFetcher) from web server uses HTTP Range Requests. Range request may fail if your server does not support ranges, if it's not configured properly or if the fetched file is smaller than the first chunk size. Test your web server or disable chunked reader with {chunked: false} when in doubt.

options.firstChunkSize

Type: number
Default: 512 Bytes in Node / 65536 (64 KB) in browser

Size (in bytes) of the first chunk that probes the file for traces of exif or metadata.

In browser, it's usually better to read just a larger chunk in hope that it contains the whole EXIF (and not just the beginning) instead of loading multiple subsequent chunks. Whereas in Node.js it's preferable to read as little data as possible and fs.read() does not cause slowdowns.

options.chunkSize

Type: number
Default: 65536 Bytes (64 KB)

Size of subsequent chunks that may be read after the first chunk.

options.chunkLimit

Type: number
Default: 5

Max amount of subsequent chunks allowed to read in which exifr searches for data segments and blocks. I.e. failsafe that prevents from reading the whole file if it does not contain all of the segments or blocks requested in options.

This limit is bypassed if multi-segment segments ocurs in the file and if options.multiSegment allows reading all of them.

If the exif isn't found within N chunks (64*5 = 320KB) it probably isn't in the file and it's not worth reading anymore.

Output format

options.mergeOutput

Type: bool
Default: true

Merges all parsed segments and blocks into a single object.

Warning: mergeOutput: false should not be used with translateKeys: false or when parsing both ifd0 (image) and ifd1 (thumbnail). Tag keys are numeric, sometimes identical and may collide.

mergeOutput: false mergeOutput: true
{
  Make: 'Google',
  Model: 'Pixel',
  FNumber: 2,
  Country: 'Czech Republic',
  xmp: '<x:xmpmeta><rdf:Description>...'
}
{
  ifd0: {
    Make: 'Google',
    Model: 'Pixel'
  },
  exif: {
    FNumber: 2
  },
  iptc: {
    Country: 'Czech Republic'
  },
  xmp: '<x:xmpmeta><rdf:Description>...'
}

options.sanitize

Type: bool
Default: true

Cleans up unnecessary, untransformed or internal tags (IFD pointers) from the output.

options.silentErrors

Type: bool
Default: true

Error messages are stored at output.errors instead of thrown as Error instances and causing promise rejection.

Failing silently enables reading broken files. But only file-structure related errors are caught.

options.translateKeys

Type: bool
Default: true

Translates tag keys from numeric codes to understandable string names. I.e. uses Model instead of 0x0110. Most keys are numeric. To access the Model tag use output.ifd0[0x0110] or output.ifd0[272] Learn more about dictionaries.

Warning: translateKeys: false should not be used with mergeOutput: false. Keys may collide because ICC, IPTC and TIFF segments use numeric keys starting at 0.

translateKeys: false translateKeys: true
{
  0x0110: 'Pixel', // IFD0
  90: 'VsetΓ­n', // IPTC
  64: 'Perceptual', // ICC
  desc: 'sRGB IEC61966-2.1', // ICC
}
{
  Model: 'Pixel', // IFD0
  City: 'VsetΓ­n', // IPTC
  RenderingIntent: 'Perceptual', // ICC
  ProfileDescription: 'sRGB IEC61966-2.1', // ICC
}

options.translateValues

Type: bool
Default: true

Translates tag values from raw enums to understandable strings. Learn more about dictionaries.

translateValues: false translateValues: true
{
  Orientation: 1,
  ResolutionUnit: 2,
  DeviceManufacturer: 'GOOG'
}
{
  Orientation: 'Horizontal (normal)',
  ResolutionUnit: 'inches',
  DeviceManufacturer: 'Google'
}

options.reviveValues

Type: bool
Default: true

Converts dates from strings to a Date instances and modifies few other tags to a more readable format. Learn more about dictionaries.

reviveValues: false reviveValues: true
{
  GPSVersionID: [0x02, 0x02, 0x00, 0x00],
  ModifyDate: '2018:07:25 16:34:23',
}
{
  GPSVersionID: '2.2.0.0',
  ModifyDate: <Date instance: 2018-07-25T14:34:23.000Z>,
}

Advanced APIs

Tips for advanced users. You don't need to read further unless you're into customization and bundlers.

Modularity, Pugin API, Configure custom bundle

This is mostly relevant for Web Browsers, where file size and unused code elimination is important.

The library's functionality is divided into four categories.

  • (Chunked) File reader reads different input data structures by chunks.
    BlobReader (browser), UrlFetcher (browser), FsReader (Node.js), Base64Reader
    See src/file-parsers/.
    NOTE: Everything can read everything out-of-the-box as a whole file. But file readers are needed to enable chunked mode.
  • File parser looks for metadata in different file formats
    .jpg, .tiff, .heic
    See src/file-parsers/.
  • Segment parser extracts data from various metadata formats (JFIF, TIFF, XMP, IPTC, ICC)
    TIFF/EXIF (IFD0, EXIF, GPS), XMP, IPTC, ICC, JFIF
    See src/segment-parsers/.
  • Dictionary affects the way the parsed output looks.
    See src/dicts/.

Each reader, parser and dictionary is a separate file that can be used independently. This way you can configure your own bundle with only what you need, eliminate dead code and save tens of KBs of unused dictionaries.

Check out examples/custom-build.js.

Scenario 1: We'll be handling .jpg files in blob format and we want to extract ICC data in human-readable format. For that we'll need dictionaries for ICC segment.

// Core bundle has nothing in it
import * as exifr from 'exifr/src/core.mjs'
// Now we import what we need
import 'exifr/src/file-readers/BlobReader.mjs'
import 'exifr/src/file-parsers/jpeg.mjs'
import 'exifr/src/segment-parsers/icc.mjs'
import 'exifr/src/dicts/icc-keys.mjs'
import 'exifr/src/dicts/icc-values.mjs'

Scenario 2: We want to parse .heic and .tiff photos, extract EXIF block (of TIFF segment). We only need the values to be translated. Keys will be left untranslated but we don't mind accessing them with raw numeric keys - output[0xa40a] instead of output.Sharpness. Also, we're not importing any (chunked) file reader because we only work with Uint8Array data.

import * as exifr from 'exifr/src/core.mjs'
import 'exifr/src/file-parsers/heic.mjs'
import 'exifr/src/file-parsers/tiff.mjs'
import 'exifr/src/segment-parsers/tiff.mjs'
import 'exifr/src/dicts/tiff-exif-values.mjs'
Translation dictionaries, customization

EXIF Data are mostly numeric enums, stored under numeric code. Dictionaries are needed to translate them into meaningful output. But they take up a lot of space (40 KB out of full build's 60 KB). So it's a good idea to make your own bundle and shave off the dicts you don't need.

  • Key dict translates object keys from numeric codes to string names (output.Model instead of output[0x0110])
  • Value dict translates vales from enum to string description (Orientation becomes 'Rotate 180' instead of 3)
  • Reviver further modifies the value (converts date string to an instance of Date)

Exifr's dictionaries are based on exiftool.org. Specifically these: TIFF (EXIF & GPS), ICC, IPTC, JFIF

// Modify single tag's 0xa409 (Saturation) translation
import exifr from 'exifr'
let exifKeys   = exifr.tagKeys.get('exif')
let exifValues = exifr.tagValues.get('exif')
exifKeys.set(0xa409, 'Saturation')
exifValues.set(0xa409, {
  0: 'Normal',
  1: 'Low',
  2: 'High'
})
// Modify single tag's GPSDateStamp value is processed
import exifr from 'exifr'
let gpsRevivers = exifr.tagRevivers.get('gps')
gpsRevivers.set(0x001D, rawValue => {
  let [year, month, day] = rawValue.split(':').map(str => parseInt(str))
  return new Date(year, month - 1, day)
})
// Create custom dictionary for GPS block
import exifr from 'exifr'
exifr.createDictionary(exifr.tagKeys, 'gps', [
  [0x0001, 'LatitudeRef'],
  [0x0002, 'Latitude'],
  [0x0003, 'LongitudeRef'],
  [0x0004, 'Longitude'],
])
// Extend existing IFD0 dictionary
import exifr from 'exifr'
exifr.createDictionary(exifr.tagKeys, 'ifd0', [
  [0xc7b5, 'DefaultUserCrop'],
  [0xc7d5, 'NikonNEFInfo'],
  ...
])
Usage with Webpack, Parcel, Rollup, Gatsby, etc... Under the hood exifr dynamically imports Node.js fs module. The import is obviously only used in Node.js and not triggered in a browser. But your bundler may, however, pick up on it and fail with something like Error: Can't resolve 'fs'.

Parcel works out of the box and Webpack should too because of webpackIgnore magic comment added to the library's source code import(/* webpackIgnore: true */ 'fs').

If this does not work for you, try adding node: {fs: 'empty'} and target: 'web' or target: 'webworker' to your Webpack config. Or similar settings for your bundler of choice.

Alternatively, create your own bundle around core build and do not include FsReader in it.

Exifr is written using modern syntax, mainly async/await. You may need to add regenerator-runtime or reconfigure babel.

Performance

Tips for better performance

Here are a few tips for when you need to squeeze an extra bit of speed out of exifr when processing a large amount of files. Click to expand.

Use options.pick if you only need certain tags Unlike other libraries, exifr can only parse certain tags, avoid unnecessary reads and end when the last picked tag was found.
// do this:
let {ISO, FNumber} = await exifr.parse(file, {exif: ['ISO', 'FNumber']})
// not this:
let {ISO, FNumber} = await exifr.parse(file)
Disable options.ifd0 if you don't need the data Even though IFD0 (Image block) stores pointers to EXIF and GPS blocks and is thus necessary to be parsed to access said blocks. Exifr doesn't need to read the whole IFD0, it just looks for the pointers.
// do this:
let options = {ifd0: false, exif: true} 
// not this:
let options = {exif: true} 
Use exifr.gps() if you only need GPS If you only need to extract GPS coords, use exifr.gps() because it is fine-tuned to do exactly this and nothing more. Similarly there's exifr.orientation().
// do this:
exifr.gps(file)
// not this:
exifr.parse(file, {gps: true})
Cache options object If you parse multiple files with the same settings, you should cache the options object instead of inlining it. Exifr uses your options to create an instance of Options class under the hood and uses WeakMap to find previously created instance instead of creating q new one each time.
// do this:
let options = {exif: true, iptc: true}
for (let file of files) exif.parse(file, options)
// not this:
for (let file of files) exif.parse(file, {exif: true, iptc: true})

Remarks

File reading: You don't need to read the whole file and parse through a MBs of data. Exifr takes an educated guess to only read a small chunk of the file where metadata is usually located. Each platform, file format, and data type is approached differently to ensure the best performance.

Finding metadata: Other libraries use brute force to read through all bytes until 'Exif' string is found. Whereas exifr recognizes the file structure, consisting of segments (JPEG) or nested boxes (HEIC). This allows exifr to read just a few bytes here and there, to get the offset and size of the segment/box and pointers to jump to the next.

HEIC: Simply finding the exif offset takes 0.2-0.3ms with exifr. Compare that to exif-heic-js which takes about 5-10ms on average. Exifr is up to 30x faster.

Benchmarks

Try the benchmark yourself at benchmark/chunked-vs-whole.js

user reads file            8.4 ms
exifr reads whole file     8.2 ms
exifr reads file by chunks 0.5 ms  <--- !!!
only parsing, not reading  0.2 ms  <--- !!!

Observations from testing with +-4MB pictures (Highest quality Google Pixel photos. Tested on a mid-range dual-core i5 machine with SSD).

  • Node: Parsing after fs.readFile = 0.3ms
  • Node: Reading & parsing by chunks = 0.5ms
  • Browser: Processing ArrayBuffer = 3ms
  • Browser: Processing Blob = 7ms
  • Browser: <img> with Object URL = 3ms
  • Drag-n-dropping gallery of 100 images and extracting GPS data takes about 65ms.
  • Phones are about 4x slower. Usually 4-30ms per photo.

Be sure to visit the exifr playground or benchmark/gps-dnd.html, drop in your photos and watch the parsed in timer.

Changelog

For full changelog visit CHANGELOG.md.

Notable changes

  • 6.0.0 & 6.2.0 PNG support.
  • 4.3.0 Package.json's "main" now points to UMD bundle for better compatibility.
  • 4.1.0 Started bundling shims and polyfills with legacy builds. Suppporting IE10.
  • 4.0.0 Added XMP Parser and XMP Extended support.
  • 3.0.0 Major rewrite, added ICC parser, HEIC file support, IE11 back compat, reimplemented chunked reader.

F.A.Q.

Why are there different kB sizes on npm, bundlephobia, and badge in the readme?

TL;DR: Because exifr comes in three bundles, each in three format variants (ESM, UMD, legacy), each in two extensions (.js and .mjs or .mjs) due to tooling. Plus source codes are included.

npm (~1MB, ~65 files): The module includes both src/ and dist/. That's source codes of all the readers, parsers and dictionaries. Multiplied by 3 bundles (full, lite, mini). Then multiplied by 3 bundle formats (ESM, UMD, legacy for IE10) and multiplied by 2 extensions (.mjs+.js or .cjs+.js). But you won't use all of the files. They're there so you can choose what's best for your project, tooling and environment.

bundlephobia (~63/22 kB): Full build is the "main" entry point (in package.json) picked up by Node and bundlephobia. But it's meant for use in Node where size doesn't matter.

badge in readme (~9 kB): The badge points to mini bundle which contains the bare minimum needed to cover the most use-cases (get orientation, coords, exif info, etc...). This is meant for browsers where file size matters.

Contributing

Contributions are welcome in any form. Suggestions, bug reports, docs improvements, new tests or even feature PRs. Don't be shy, I don't bite.

If you're filing an issue, please include:

  • The photo that's missing metadata or causing the bug
  • Repo or a sandbox (like this one) with minimal code where the bug is reproducible.

There are so many environments, tools and frameworks and I can't know, nor try them all out. Letting me peek into your setup makes tracking down the problem so much easier.

PRs are gladly accepted. Please run tests before you create one:

  • in browser by visiting /test/index.html (uses import maps, you may need to enable experimental flags in your browser)
  • in Node.js by running npm run test

License

MIT, Mike KovaΕ™Γ­k, Mutiny.cz

Comments
  • Compatibility with .tif/.tiff files

    Compatibility with .tif/.tiff files

    Really nice and clean written library, best exif parse I've found so far! Works awesome with .jpg images.

    But when trying to parse a .tif file, no error is thrown and the result from the parsing is 'undefined'. Is the parser not compatible with tif files?

    opened by BossensM 31
  • (node:68141) Warning: Closing file descriptor NNN on garbage collection

    (node:68141) Warning: Closing file descriptor NNN on garbage collection

    This warning appears when I am trying to read exif data from a large collection of files. It seems like Exifr does not close file handles.

    Example code:

    await exifr.parse("filename.jpeg", true)
    
    opened by aradzie 10
  • import('fs') fails with webpack

    import('fs') fails with webpack

    Hello, does exifr require a node server or can it be used on a php server running a React application? I noticed if requires fs.

    if (isNode) {
    		if (typeof require === 'function')
    			var fsPromise = Promise.resolve(require('fs').promises);
    		else
    			var fsPromise = import('fs').then(module => module.promises);
    	}
    
    opened by mantismamita 10
  • Sidecar support

    Sidecar support

    Is there any plan to officially supporting sidecar files?

    There are multiple type of sidecar files: XMP, exif, etc. These files live next to the original media file and should have the same format as the data in a jpg itself. It is a good way to attach metadata to e.g: videos.

    Here is how exiftool deals with it: https://exiftool.org/metafiles.html Here is an other lib for xmp sidecar reading that has some test cases: https://github.com/PhilipF5/xmp-sidecar/tree/master/test

    I also generated some: I used exiftool with: exiftool.exe -xmp -b IMG_5910.jpg > IMG_5910.xmp exiftool.exe -exif-b IMG_5910.jpg > IMG_5910.exif The output: IMG_5910.jpg IMG_5910

    IMG_5910.exif: IMG_5910.exif.txt IMG_5910.xmp: IMG_5910.xmp.txt

    I dropped the .exif to https://mutiny.cz/exifr/ and seems to work without an issue. .xmp unfortunately not.

    Context of the feature request: We are currently replacing exif processing (https://github.com/bpatrik/pigallery2/issues/277) for pigallery2. Your nice project looks really promising: https://github.com/bpatrik/pigallery2/issues/277#issuecomment-836948216. We would like a lib that can support sidecars too https://github.com/bpatrik/pigallery2/issues/156. Unfortunately, our only option is exiftool currently: https://github.com/bpatrik/pigallery2/pull/294#discussion_r629922316

    opened by bpatrik 9
  • Binary data / garbage characters extracted from IPTC fields

    Binary data / garbage characters extracted from IPTC fields

    I'm not sure what this photographer is doing (in Lightroom, I believe) but some of the photos they produce have this problem. Most do not.

    I've attached a file here that shows the funky data in the IPTC "Object Name" field. I have 5 other examples, and can share them privately if you like.

    I'm not able to speak w/the photographer and can't determine if they are doing something wrong or maybe exifr is. Would you be able to provide some insight?

    binary-data-in-iptc-object-name-field

    opened by sunild 7
  • XMP missing

    XMP missing

    Hey there!

    Thanks for your great work, out of all Exif-parsers for NodeJS yours works best.

    Unluckily, I've got some issues reading XMP of my Mavic 2 Drone Footage. When I use your page (https://exifr.netlify.com/), it works like a charm and XMP-APP1 output is shown, but when I try it in NodeJS on my project, it'll fail with the same picture as input. Any clues?

    function parseAsync (source) {
      return new Promise(
        function (resolve, reject) {
          parse(source, { xmp: true, gps: true, mergeOutput: false, exif: false })
            .then(
              result => {
                console.log(result)
                resolve({ latitude: result.gps.latitude, longitude: result.gps.longitude, altitude: result.gps.GPSAltitude }
                )
              })
            .catch()
        }
      )
    }
    

    2019-10-16

    Best regards

    Florian

    opened by florian-senn 7
  • EXIF info not recognized in HEIC image

    EXIF info not recognized in HEIC image

    In this photo Sample.heic, taken by Samsung Galaxy A40, there was not found any EXIF info using this library. I know there is EXIF info in it as Windows 10 and imagemagick can see it.

    opened by mshgh 6
  • read() does not work

    read() does not work

    Current version on node v.14.10.1

    'use strict'
    const exifr = require('exifr')
    , file = process.argv[2]
    , exr = new exifr.Exifr()
    ;(async () => {
    	const result = await exr.read(file)
    	console.log(result)
    })()
    

    Output:

    undefined
    

    What am I doing wrong?

    opened by akidee 5
  • "RangeError: Invalid typed array length: 59103"

    I'm using release 7.0.0 on Node.js 15.14.0.

    It works very well with most photos (thanks! πŸ™) , but when I try with one specific image, I get this error:

    RangeError: Invalid typed array length: 59103
            at new Uint8Array (<anonymous>)
            at I.getUint8Array (/<project-path>/node_modules/exifr/dist/full.umd.js:1:3701)
            at I.getLatin1String (/<project-path>/node_modules/exifr/dist/full.umd.js:1:3866)
            at Mt.parse (/<project-path>/node_modules/exifr/dist/full.umd.js:1:72319)
            at /<project-path>/node_modules/exifr/dist/full.umd.js:1:12692
            at Array.map (<anonymous>)
            at ue.executeParsers (/<project-path>/node_modules/exifr/dist/full.umd.js:1:12663)
            at async ue.parse (/<project-path>/node_modules/exifr/dist/full.umd.js:1:12349)
            at async /<project-path>/_scripts/sync-photos.js:46:22
    

    You can find the photo here: https://github.com/nhoizey/nicolas-hoizey.photo/blob/main/src/photos/stairz/stairz.jpg

    It works well on https://mutiny.cz/exifr/

    I tried many different option settings.

    It works with these

    Everything but iptc

    let exifrOptions = {
      iptc: false,
    };
    

    Only gps, full:

    let exifrOptions = {
      ifd0: true,
      ifd1: true,
      exif: true,
      gps: true,
      iptc: false,
    };
    

    Only iptc, full:

    let exifrOptions = {
      ifd0: false,
      ifd1: false,
      exif: false,
      gps: false,
      iptc: true,
    };
    

    Only iptc, without AudioType (but I still get AudioType in the result πŸ€·β€β™‚οΈ):

    let exifrOptions = {
      ifd0: false,
      ifd1: false,
      exif: false,
      gps: false,
      iptc: { skip: ['AudioType'] },
    };
    

    But I get the error with these

    iptc, others default:

    let exifrOptions = {
      iptc: true,
    };
    

    iptc and gps:

    let exifrOptions = {
      ifd0: false,
      ifd1: false,
      exif: false,
      gps: true,
      iptc: true,
    };
    

    Everything:

    let exifrOptions = {
      ifd0: true,
      ifd1: true,
      exif: true,
      gps: true,
      iptc: true,
    };
    

    It looks quite similar to #31 and #44

    opened by nhoizey 4
  • Urls with query string are not working correctly. This fixes it.

    Urls with query string are not working correctly. This fixes it.

    I am getting 403 from aws s3 as query_string are not passed through in the node fetch polyfill.

    With this PR, I am able to parse the exifs on files stored on AWS S3 with presigned_urls. (It is another but simpler use case of #57)

    From Node documentation:

    path Request path. Should include query string if any. E.G. '/index.html?page=12'. An exception is thrown when the request path contains illegal characters. Currently, only spaces are rejected but that may change in the future. Default: '/'.

    opened by ombr 4
  • Region support

    Region support

    Hi,

    I'm looking for a "one exif reader above all" for my home project https://github.com/bpatrik/pigallery2 (currently I'm using 4). Exifr looks really promising.

    After a quick testing, I see that unfortunately it does not support regions: kΓ©p (From: http://metapicz.com/)

    It recoginses the Area, but does not attach the Area label (i.e.: the type of the area, e.g.: face and the name of the area eg.: Name of the person) to it: kΓ©p

    This feature is needed to get multiple, tagged people (faces) from the photo.

    Is there any plan adding this feature?

    Thank you!

    If helps, here is how I get this value using exifreader: https://github.com/bpatrik/pigallery2/blob/36ecbcbd3d5d3e659ddf20add456982dcb960d3d/src/backend/model/threading/MetadataLoader.ts#L221-L270

    I used this photo for testing: https://github.com/bpatrik/pigallery2/blob/master/demo/images/IMG_6253.jpg

    opened by bpatrik 4
  • Using exifr.parse on a buffer that contains just exif data

    Using exifr.parse on a buffer that contains just exif data

    Heya!

    Is it possible to have exifr.parse function on a buffer that just contains exif data rather than a whole file?

    I'm currently relying on Sharp to gather some file metadata (like width/height/orientation), and would like to use Exifr to (optionally) parse the Exif information if Sharp returns it. I do not have a Buffer with the full file contents available (nor am I able to get it), so I was hoping I could stream the file to sharp, have sharp prepare an Exif buffer, and parse that out with Exifr. For example:

    import { createReadStream } from 'node:fs';
    import { pipeline } from 'node:stream/promises';
    import sharp from 'sharp';
    import { parse } from 'exifr';
    
    const stream = createReadStream('./path/to/large/file.jpg');
    
    const getMetadata = (stream) => new Promise((resolve, reject) => {
      pipeline(stream, sharp().metadata(async (err, sharpMetadata) => {
        if (err) return reject(err);
        
        // Do stuff with sharp metadata
    
        if (sharpMetadata.exif) {
          const exifData = await parse(sharpMetadata.exif);
          //               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        }
    
        // etc
      });
    });
    

    At the moment, the highlighted section errors with an error: Error: Unknown file format, which I believe stems from the fact that exifr expects the buffer to contain all file data, rather than just the exif information.

    opened by rijkvanzanten 1
  • Missing/wrong Rating meta?

    Missing/wrong Rating meta?

    I'm trying to use this tool to read ratings of images, but there seems to be a big discrepancy I can't figure out where different Exif readers and programs shows different results.

    I've attached an image used for the following results:

    • Windows Explorer: 1
    • Adobe Bridge:
      • In UI: 2
      • In File Info -> Raw:
        • xmp:Rating: 2
        • MicrosoftPhoto:Rating: 1
    • Exiftool (program):
      • Rating: 2
      • Rating Percent: 1
    • Exif Data (online tool):
      • IFD0
        • Rating: 1
        • Rating Percent: 1
      • XMP -xmp
        • Rating: 2
      • XMP -microsoft
        • Rating: 1
    • exifr (Online):
      • IFD0
        • Rating: 1
        • Rating Percent: 1
      • XMP
        • Rating: 1
    • exifr (.parse(file, true) to JSON file):
      • Rating: 1
      • Rating Percent: 1

    I am not quite sure which one to go with here, but there seem to be some information differences. I tested on a different computer for both Explorer and Adobe Bridge to try to rule out cache as much as possible, and there are no hidden sidecar files either.

    Image used for these results

    opened by KeyboardRage 0
  • Parsing IPTC from TIFF throws error

    Parsing IPTC from TIFF throws error

    Hi There,

    Thanks for this awesome lib! Having some issues with parsing IPTC from some TIFF files - specifically those generated by Lightroom Classic.

    Resaving the files in another application removes the error. Included here is a file that throws these sorts of errors when parsed in exif: Error: Invalid input argument for BufferView: 5898524,1193614083,540,470024194,24...

    Error does not happen if you exclude IPTC in the options. For reference, exiftool can parse this file without error.

    I'm using version 7.1.3

    Hope this helps.

    Cheers

    badIPTC.tif.zip

    opened by bentron2000 0
  • Expose TIFF header start and position of Makernote tag

    Expose TIFF header start and position of Makernote tag

    Background I'd like to code an extension to decode the makernotes (starting with Nikon, later Canon, Sony & Fuji where possible). Such implementations exist in Perl and Java but - as far as I know - not for Javascript. Makernotes (at least for Nikon, haven't checked other brands yet) have again an IFD which references the actual values. Issue is that the Makernotes IFD entries reference an offset relative to the start of the TIFF header. So I need this and the actual start of the makernote tag to calculate the correct offset within the makernotes section.

    Request Expose the offset / start of the TIFF header and the makernotes tag

    opened by phhoehne 0
Owner
Mike
Democratizing multi-platform development, one commit at a time.
Mike
SpotLight is a web app which uses spotify developer APIs and lists your most listened songs, albums, artists and genres.

SpotLight is a web app which uses spotify developer APIs and lists your most listened songs, albums, artists and genres.

Sahil Saha 9 Dec 31, 2022
Discord.js V14 Slash-Command and Context-Menu Handler. Most efficient and advanced Handler out there!

Base discord.js v14 Command Handler Written by Tomato6966 Made for Deezcord Features βœ” Slash Commands βœ” Sub-Slash Commands βœ” Group-Sub-Slash Commands

Tomato6966 9 Dec 21, 2022
Randomfy takes your most listened Spotify artists

Randomfy takes your most listened Spotify artists, selects one of them and shows you similar artists, so you can meet new artists based on the previous selected one.

Raul Andrade 9 Nov 6, 2022
A Javascript library for working with Audio. It provides a consistent API for loading and playing audio on different browsers and devices. Currently supports WebAudio, HTML5 Audio, Cordova / PhoneGap, and a Flash fallback.

SoundJS SoundJS is a library to make working with audio on the web easier. It provides a consistent API for playing audio in different browsers, inclu

CreateJS 4.3k Dec 31, 2022
This is a simple web based media player for playing video and audio. Build with pure HTML, CSS and Javascript. No framework or library included.

Aim-Player This is a simple web based media player for playing video and audio. Build with pure HTML, CSS and Javascript. No framework or library incl

Aim Mikel 2 Jun 27, 2021
:musical_score: ts-audio is an agnostic library that makes it easy to work with AudioContext and create audio playlists in the browser

ts-audio Β· ts-audio is an agnostic and easy-to-use library to work with the AudioContext API and create Playlists. Features Simple API that abstracts

Evandro Leopoldino Gonçalves 284 Dec 25, 2022
VexFlow 3 - A JavaScript library for rendering music notation and guitar tablature.

VexFlow 3 - A JavaScript library for rendering music notation and guitar tablature.

Mohit Cheppudira 3.5k Jan 6, 2023
A Node JS Express/Serverless demo application that creates a slideshow video using the Pexels image library and Shotstack video editing API.

Shotstack Pexels Slideshow Video Demo This project demonstrates how to use the Shotstack cloud video editing API to create a video using an HTML form

Shotstack 25 Dec 9, 2022
Aviatojs - A simple library to trim, cut and join audio files.

Aviatojs A simple library to trim, cut and join audio files. Usage For a fully working example refer to this example Importing import {AviatoAudio}

null 6 Oct 7, 2022
Library to calculate a Mean Opinion Score (MOS) from 1 to 5 for audio and video real time communications

RTC SCORE Library to calculate a Mean Opinion Score (MOS) from 1 to 5 for audio and video real time communications. The first version of the algorithm

Gustavo Garcia 25 Nov 27, 2022
Vio-MD is multi-device whatsapp bot using library @adiwajshing/baileys and example bot of Violetics API

Vio Multi Device WhatsApp Bot Use at your own risk! Build with Baileys and JavaScript's native Map class ( as a command handler ), using https://viole

Violetics 4 May 31, 2022
Minimalist Virtual DOM library with JSX and factory pattern for stateful components.

Reflex Reflex JS is a tiny virtual-dom library with factory based functional components. See this example running See more complex demos Table of cont

Alexis Bouhet 14 Sep 2, 2022
:loudspeaker: A JavaScript library to add voice commands to your sites, apps or games.

Voix JS A JavaScript library to add voice commands to your sites, apps or games. NOTE: At this time, this library is only compatible with Google Chrom

Guille Paz 548 Dec 8, 2022
AmplitudeJS: Open Source HTML5 Web Audio Library. Design your web audio player, the way you want. No dependencies required.

Documentation β€’ Examples β€’ Tutorials β€’ Support Us β€’ Get Professional Help AmplitudeJS is a lightweight JavaScript library that allows you to control t

Server Side Up 3.9k Jan 2, 2023
A web video player built for the HTML5 world using React library.

video-react Video.React is a web video player built from the ground up for an HTML5 world using React library. Installation Install video-react and pe

null 2.4k Jan 6, 2023
A Discord bot library to make the development of a bot a little bit easier.

Discord Bot Framework (DBF) A basic Discord bot framework to allow for easier creation of bots. With this library, you can easily parse and handle com

Yoo-Babobo 1 Dec 23, 2021
Buzz, a Javascript HTML5 Audio library

Buzz is a small but powerful Javascript library that allows you to easily take advantage of the new HTML5 audio element. It tries to degrade silently on non-modern browsers.

Jay Salvat 1.2k Dec 10, 2022
A simple library for using the JavaScript Full Screen API.

BigScreen A simple library for using the JavaScript Fullscreen API. Why should I use it? BigScreen makes it easy to use full screen on your site or in

Brad Dougherty 712 Dec 22, 2022
HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.

HLS.js is a JavaScript library that implements an HTTP Live Streaming client. It relies on HTML5 video and MediaSource Extensions for playback. It wor

video-dev 12.3k Jan 2, 2023