Library / SDK
aadsm/jsmediatags avatar
aadsm/jsmediatags

jsmediatags reads ID3, MP4 and FLAC metadata straight from the browser

Media Tags Reader (ID3, MP4, FLAC)

797 stars124 forksJavaScriptNOASSERTION

At a glance

What is it?
jsmediatags is a JavaScript library that parses ID3v1, ID3v2, MP4 and FLAC tags in Node, the browser and React Native, returning both raw tag frames and human readable shortcuts for common fields.
Who is it for?
Adopt jsmediatags when a browser or React Native app needs to display title, artist, album or cover art from a local file or a remote URL without shipping a server side metadata service. Do not adopt it when you need to write tags back, since the library is a reader, or when you need ID3v2 unsynchronisation handling on files with broken frames beyond what the parser covers.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 103 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Metadata parsing that runs where the file already is

Almost every web app that touches music needs one thing: the title, artist, album and cover art of a file the user just picked, or of a track streamed from a URL. The usual answers are a server side endpoint that receives the upload, or a heavyweight media framework. jsmediatags takes the third path, a reader that runs in the place the file already is. The library is described as a Media Tags Reader for ID3, MP4 and FLAC, and it is the next version of the earlier JavaScript-ID3-Reader project. Its stated file readers cover NodeJS, XMLHttpRequest, Blob, File, Buffers and Arrays, and React Native. The audience is front end developers building upload previews, playlist importers and media pickers, and mobile developers who need a track's metadata without a native module. It is a read only library by design, which is worth saying up front because that boundary decides when it is the right tool and when it is not.

Four tag formats, and the unsynchronisation problem in ID3v2

The tag readers listed are ID3v1, ID3v2 with unsynchronisation support, MP4 and FLAC. Each format has a different container layout and different field names, which is the underlying problem the library exists to smooth over. ID3v1 is the fixed block at the end of a file. ID3v2 is a frame based structure with version, size and flag fields, and the unsynchronisation support is the detail that matters most in practice, because the technique of inserting a zero byte after 0xFF exists to stop tag bytes from being mistaken for MPEG frame sync markers, and a parser that ignores it will misread files written by certain encoders. MP4 stores metadata in atoms under keys such as the tag family used by the example output, and FLAC keeps its own metadata block. Handling four containers in one library is what makes it useful for a media browser that has to deal with whatever the user happens to have, rather than a curated set of MP3s.

Two APIs: a one call read and a configurable Reader

The surface is deliberately small, with a simple form and a more configurable one. The simple API is a single call with success and error callbacks:

javascript
// Simple API - will fetch all tags
var jsmediatags = require("jsmediatags");

jsmediatags.read("./music-file.mp3", {
  onSuccess: function(tag) {
    console.log(tag);
  },
  onError: function(error) {
    console.log(':(', error.type, error.info);
  }
});

The advanced API is a Reader object that you configure before reading:

javascript
// Advanced API
var jsmediatags = require("jsmediatags");

new jsmediatags.Reader("http://www.example.com/music-file.mp3")
  .setTagsToRead(["title", "artist"])
  .read({
    onSuccess: function(tag) {
      console.log(tag);
    },
    onError: function(error) {
      console.log(':(', error.type, error.info);
    }
  });

The difference matters on large remote files. Reading every tag means parsing every frame, while setTagsToRead narrows the work to the fields you need, which is the difference between pulling a full ID3 header set over the network and pulling two strings. The error callback reports a type and an info field, so failures are meant to be handled rather than swallowed by a silent empty result.

Shortcut names sit next to the raw tag frames

The output object differs by tag type, but all of them share a common structure with a type field, a tags object, and inside that both the original tag identifiers and human readable shortcuts. Because each tag type uses different tag names for the same kind of data, the most common tags are also available under human readable names, so artist points to the data of the ID3 frame named TPE1, album points to TALB and so on. The supported shortcuts are title, artist, album, year, comment, track, genre, picture and lyrics. Keeping both forms in the same object is a deliberate choice: the shortcut gives you a field name that does not change when a file is MP4 instead of MP3, while the raw frame, with its id, size and description, stays available for anyone who needs the container's own naming. A caller who only wants a display string can ignore the frames entirely, and a caller who needs to handle an exotic field can find it without a second parse.

Album art arrives as bytes and a content type

The picture shortcut is not a URL or an image element, it is raw bytes. The picture tag contains an array buffer of all the bytes of the album artwork image as well as the content type of the image, and the documentation shows the conversion to something an img element can display:

javascript
const { data, format } = result.tags.picture;
let base64String = "";
for (const i = 0; i < data.length; i++) {
  base64String += String.fromCharCode(data[i]);
}
img.src = `data:${data.format};base64,${window.btoa(base64String)}`;

The loop is byte by byte rather than chunked, so on a large embedded image this is not the cheapest way to build a data URL. The format value comes straight from the tag and is interpolated into the data URL, so the caller is relying on the file's own declaration of its content type. A more defensive implementation would check the value against a whitelist of image types before handing it to an img src, and would use a chunked conversion for big artwork. The library hands you the bytes and the type, which is the right division of labour, but it does not do that validation for you.

Remote reads depend on CORS headers your server must send

This is the part of the documentation that catches teams out, because the failure looks like a network error rather than a configuration gap. When using HTTP CORS requests you need to make sure that the server is configured to receive If-Modified-Since and Range headers with the request, which can be configured by returning the Access-Control-Allow-Headers HTTP header with the OPTIONS request response. The browser also needs to be allowed to read the Content-Length and Content-Range headers, which is configured by returning the Access-Control-Expose-Headers HTTP header. The expected header is:

code
Access-Control-Allow-Headers: If-Modified-Since, Range

If your server does not send those, the browser blocks the response and the library reports an error rather than returning tags. The Range header is why a library like this can read a partial file from a remote host at all, and If-Modified-Since is how it avoids refetching unchanged data. Note also that the URI has to include the scheme, for example https://, since relative URIs are not supported.

React Native needs buffer and filesystem shims

React Native is listed as a supported file reader, but it does not work out of the box. React Native support requires some additional dependencies, installed with npm install --save jsmediatags buffer react-native-fs. The reason is visible in the package metadata, where the react-native field maps fs to false and ./NodeFileReader to false, so the Node filesystem reader is swapped out for a react-native-fs based one and the buffer package fills in the Buffer role that Node would provide natively. The usage pattern stays the same as elsewhere, with a Reader instance and the same onSuccess and onError callbacks, and the documentation also shows wrapping the reader in a Promise if you prefer promise style. The browser distribution is a separate path: copy the dist/jsmediatags.min.js file into your web application directory and include it with a script tag, or load it from cdnjs, and the UMD build gives you a global object, a CommonJS module and other usage options from the same file.

A quiet release history and a version mismatch worth knowing

Maintenance is the weak point here. The tagged releases include v3.9.5 on 2021-02-22, v3.9.4 on 2021-02-16 and v3.9.3 on 2019-11-10, while the version field in package.json reads 3.9.7, so the published package version and the last tag do not line up, and the most recent tagged release is several years old. The last push to the repository was on 2026-07-13, so work has continued without new tags. The package is built with Babel and Browserify, with Flow type annotations stripped at build time, and the scripts include a jest test command and a build command that compiles src to build2. Licensing is a little confusing on paper: the package metadata declares BSD-3-Clause, and the repository carries a LICENSE.md file, but the repository licence is recorded as a custom one that GitHub cannot classify, so read LICENSE.md before redistributing the library inside a product. For a reader that parses bytes rather than executing them, the risk of an old dependency is lower than it would be in a network-facing server, but the tag shortcut behaviour is exactly the kind of thing that can change between versions.

Editorial conclusion

Adopt jsmediatags when a browser or React Native app needs to display title, artist, album or cover art from a local file or a remote URL without shipping a server side metadata service. Do not adopt it when you need to write tags back, since the library is a reader, or when you need ID3v2 unsynchronisation handling on files with broken frames beyond what the parser covers. Verify first that your server returns the Access-Control-Allow-Headers and Access-Control-Expose-Headers values the library expects, because remote reads fail without them, and that the version you pin matches the tag shortcuts you rely on.

Frequently asked questions

How do I install jsmediatags and read tags from a file?

The README gives the install as npm install jsmediatags --save, then you call jsmediatags.read with the file path and onSuccess and onError callbacks. The advanced form uses new jsmediatags.Reader(path).setTagsToRead([...]).read({...}).

Which audio tag formats does jsmediatags support?

The tag readers are ID3v1, ID3v2 with unsynchronisation support, MP4 and FLAC. Files can be read through NodeJS, XMLHttpRequest, Blob, File, Buffers and Arrays, and React Native.

Why does reading a remote file fail in the browser?

The server must allow the request headers the library sends and expose the response headers it reads. The documentation expects Access-Control-Allow-Headers: If-Modified-Since, Range on the OPTIONS response, and the Access-Control-Expose-Headers header so the browser can read Content-Length and Content-Range. The URI must also include the scheme, since relative URIs are not supported.

How do I get the album cover image from jsmediatags?

The picture tag holds an array buffer of the artwork bytes plus the image content type, so you convert the bytes to a base64 string yourself and use it in a data URL with the format value as the MIME type.

Official sources

  1. aadsm/jsmediatags on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aadsm-jsmediatags.svg)](https://hysenlabs.com/projects/aadsm-jsmediatags)

Was this useful?