Skip to content

Instantly share code, notes, and snippets.

Created June 28, 2012 17:17
Show Gist options
  • Save lakenen/3012623 to your computer and use it in GitHub Desktop.
Save lakenen/3012623 to your computer and use it in GitHub Desktop.
JavaScript animated GIF detection!
function isAnimatedGif(src, cb) {
var request = new XMLHttpRequest();'GET', src, true);
request.responseType = 'arraybuffer';
request.addEventListener('load', function () {
var arr = new Uint8Array(request.response),
i, len, length = arr.length, frames = 0;
// make sure it's a gif (GIF8)
if (arr[0] !== 0x47 || arr[1] !== 0x49 ||
arr[2] !== 0x46 || arr[3] !== 0x38)
//ported from php
//an animated gif contains multiple "frames", with each frame having a
//header made up of:
// * a static 4-byte sequence (\x00\x21\xF9\x04)
// * 4 variable bytes
// * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)
// We read through the file til we reach the end of the file, or we've found
// at least 2 frame headers
for (i=0, len = length - 9; i < len, frames < 2; ++i) {
if (arr[i] === 0x00 && arr[i+1] === 0x21 &&
arr[i+2] === 0xF9 && arr[i+3] === 0x04 &&
arr[i+8] === 0x00 &&
(arr[i+9] === 0x2C || arr[i+9] === 0x21))
// if frame count > 1, it's animated
cb(frames > 1);
Copy link

atk commented Jun 29, 2012

Smaller solution (inside xhr callback):

var img = request.response;
if (!/^GIF8[79]a/) { cb(false); } else {
var frames = 0;
img.replace(/\x00\x21\xF9\x04\x00[\x2c\x21]/g, function(){ frames++ });

In addition, this only works if the domain origin of the image is the same as the page you are loading the script from.

Copy link

sathio commented Jul 12, 2013

looks like it's never satisfying the for conditions if the GIF is not animated.
can't understand why

Copy link

It is because multiple comma-separated conditions in a for loop like this

for (i=0, len = length - 9; i < len, frames < 2; ++i)

will evaluate both checks, but use only the last one for determining whether to run the loop again. So unless more than one frame is found, i will just grow and frames < 2 never satisfy.

In this case, better to use || (or have an inner loop)

Copy link

Implemented @olapeter's suggestion here, only using && since the logic is inverted between the comment and the if statement.

Also updated to support variable length extension control blocks.

Copy link

N.B. the img.replace(/\x00\x21\xF9\x04\x00[\x2c\x21]/g solution suffers the same problem as the original code here -- if the ECB isn't 4 bytes this regex won't find the frame (see the wikipedia article examples).

Copy link

great rubust solution from @marckubischta ! thanks for that.
anyone able to come up with detecting animation for webp image format?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment