Scrub Animated GIFs with JavaScript – GifScratcher

Category: Animation , Javascript | November 3, 2026
Authorlinuslundahl
Last UpdateNovember 3, 2026
LicenseMIT
Views0 views
Scrub Animated GIFs with JavaScript – GifScratcher

GifScratcher is a vanilla JavaScript library for scrubbing animated GIFs by moving the pointer across an image.

It supports automatic playback and optional transport controls, and includes a standalone player for horizontal sprite strips.

Features

  • The original GIF <img> remains visible when JavaScript is disabled.
  • Moving or dragging the pointer across the canvas selects GIF frames in either direction.
  • Optional play/pause buttons, frame stepping, and a seek slider.
  • A configurable scrub cursor and loading indicator.
  • Playback speed and interaction settings can change after initialization.
  • GIF decoding through ImageDecoder where available, with a bundled gifuct-js fallback.

How To Use It

Installation

Include dist/gifscratcher.js and dist/gifscratcher.css in your web page.

<link rel="stylesheet" href="/assets/gifscratcher/gifscratcher.css">
<script type="module">
  import { GifScratcher } from '/assets/gifscratcher/gifscratcher.js';
  new GifScratcher('#gif-demo');
</script>

Basic Usage

Place an animated .gif inside the target element and initialize GifScratcher after the markup.

<div id="gif-demo">
  <img src="/images/rotation.gif" alt="A product rotating in place">
</div>

All Configuration Options

Initialize the GIF player with new GifScratcher(target, options?). The target argument accepts a CSS selector or an HTMLElement. The optional options object accepts these settings:

  • gif (string, default ''): URL of a real animated GIF. An explicit URL takes precedence over the first <img> inside the target. The source is loaded during initialization.
  • auto (boolean, default true): Starts automatic playback and resumes it after the pointer leaves the canvas.
  • scrub (boolean, default true): Enables pointer-based frame selection on hover or drag.
  • cursor (boolean, default true): Adds the custom scrub cursor.
  • loader (boolean, default true): Displays a spinner during GIF loading and decoding.
  • player (boolean, default false): Adds play/pause, previous/next-frame buttons, a seek slider, and a frame counter.
  • speed (number, default 1): Playback speed multiplier.

Playback Methods And State

  • play(): Starts or resumes automatic playback.
  • stop(): Stops playback at the current frame.
  • render(index: number): Displays a zero-based frame index, wrapping values outside the frame count.
  • rewind(): Displays the previous frame.
  • forward(): Displays the next frame.
  • setOptions(options: Partial<GifScratcherOptions>): Changes auto, scrub, cursor, player, and speed on an existing instance. gif and loader only affect initial loading.
  • destroy(): Detaches event listeners, removes the canvas and generated controls, and releases decoded frames. It does not restore the original <img>.

Two read-only getters expose playback state:

  • frame (number): Current zero-based frame index.
  • isPlaying (boolean): true during automatic playback.

Events

Register listeners before creating the instance. Two events are dispatched on the target element:

  • gifscratcher:ready: Loading and decoding have finished. event.detail contains frames (number) and decoder (string).
  • gifscratcher:error: Loading or decoding failed. event.detail.error contains the error.

To select the final frame after loading, replace the module script in Basic Usage with this one:

<script type="module">
  import { GifScratcher } from '/assets/gifscratcher/gifscratcher.js';
  const target = document.querySelector('#gif-demo');
  target.addEventListener('gifscratcher:ready', ({ detail }) => {
    scratcher.render(detail.frames - 1);
  });
  target.addEventListener('gifscratcher:error', ({ detail }) => {
    console.error(detail.error);
  });
  const scratcher = new GifScratcher(target, {
    auto: false,
    player: true
  });
</script>

Scrub a Horizontal Sprite Strip

Keep gifscratcher.css loaded, copy dist/gifscratcher-sprite.js beside the main module, and prepare a horizontal strip of equally sized frames. The target requires a preview <img>. Set cursor: false for drag mode because the custom cursor cannot receive the pointerdown event that starts dragging.

<div id="sprite-demo">
  <img src="/images/rotation-first.jpg" alt="Product rotation preview">
</div>
<script type="module">
  import { GifScratcherSprite } from '/assets/gifscratcher/gifscratcher-sprite.js';
  new GifScratcherSprite('#sprite-demo', {
    sprite: '/images/rotation-strip.jpg',
    frames: 12,
    interaction: 'drag',
    cursor: false,
    auto: false
  });
</script>

Sprite Options

  • sprite (string, default ''): URL of the horizontal sprite image. Supply a URL to initialize the player.
  • frames (number, default 0): Number of frames in the sprite. Set a positive count matching the image strip.
  • interaction ('hover' | 'drag', default 'hover'): Scrubbing mode on devices that support hover. Touch devices use press-and-move interaction.
  • auto (boolean, default true): Starts automatic sprite playback.
  • cursor (boolean, default true): Adds a custom cursor.
  • speed (number, default 5): Delay in animation-frame ticks for sprite autoplay. Higher values slow the animation.

Sprite Methods

  • destroy(): Removes listeners, observers, and the generated cursor, and clears inline styles on the sprite image. After drag or touch interaction, the target retains touch-action: none until you clear it.
  • addCursor(): Appends the custom cursor when enabled.
  • preload(callback?): Loads the sprite image and invokes the optional callback after loading.
  • touchInteraction(): Attaches press-and-move handlers.
  • hoverInteraction(): Attaches pointer-move scrubbing.
  • dragInteraction(): Attaches press-and-drag scrubbing.
  • auto(): Starts the automatic sprite animation loop.
  • hover(): Attaches pointer-entry/exit and custom-cursor tracking.
  • resize(): Updates the element’s measured dimensions and the sprite’s frame sizing.
  • play(xcoord: number): Selects the frame at a horizontal coordinate measured from the target’s left edge, in pixels.
  • switchFrame(frame: number): Displays a zero-based sprite frame by translating the image strip.

Create a Sprite Strip

ImageMagick can extract GIF frames and join them into a single horizontal image. Set the sprite’s frames option to the number of exported frames.

magick animation.gif -coalesce frame-%03d.jpg
magick frame-*.jpg +append animation-strip.jpg

Alternatives And Related Resources

FAQs

Q: Why does a GIF from another domain fail to load?
A: The library fetches the GIF as binary data. The remote server must allow the page’s origin through CORS.

Q: Can setOptions() load another GIF?
A: No. GIF decoding happens during initialization. Call destroy(), restore the target markup, and initialize a new instance with the new gif URL.

Q: Why does the module fail when I open the HTML file directly?
A: Serve the example through a local HTTP server. ES module loading and GIF fetching are subject to browser origin restrictions.

You Might Be Interested In:


Leave a Reply