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

motion-canvas-cache

v0.1.1

Published

A generic file caching library for Motion Canvas projects with in-memory and server-side caching via Vite HMR

Downloads

241

Readme

Motion Canvas Cache

A generic file caching library for Motion Canvas projects that provides both in-memory and server-side caching via Vite's HMR WebSocket.

Features

  • Dual-layer caching: In-memory cache for fast access + server-side cache for persistence
  • Simple API: Single cached() function that handles everything
  • URL or Blob support: Cache files from URLs or directly from Blob objects
  • Auto MIME type detection: Automatically detects content type from response headers
  • Custom metadata: Store arbitrary metadata alongside cached files
  • HMR persistence: Cache persists across hot module reloads
  • Works standalone: In-memory cache works even without the server plugin
  • Generic file support: Supports any file type (audio, video, images, documents, etc.)

Installation

npm install motion-canvas-cache

Usage

1. Enable the Vite Plugin

Add the plugin to your vite.config.ts:

import {defineConfig} from 'vite';
import motionCanvas from '@motion-canvas/vite-plugin';
import {motionCanvasCachePlugin} from 'motion-canvas-cache';

export default defineConfig({
  plugins: [
    motionCanvas(),
    motionCanvasCachePlugin({
      cachePath: 'motion-canvas-cache', // Optional: default cache directory
      maxFileSize: 50, // Optional: max file size in MB (default: 50)
    }),
  ]
});

2. Use in Motion Canvas

Since Motion Canvas uses generator functions and you can't use yield* inside JSX, the pattern is:

  1. Preload assets at the beginning of your scene using yield cache()
  2. Use synchronously in JSX with cached()
import {makeScene2D, Img, Audio} from '@motion-canvas/2d';
import {cache, cached} from 'motion-canvas-cache';

export default makeScene2D(function* (view) {
  // 1. Preload all assets at the beginning using yield
  yield cache('https://example.com/image.png', {
    metadata: {width: 800, height: 600},
  });

  yield cache('https://example.com/audio.mp3', {
    metadata: {duration: 3.5},
  });

  // 2. Use synchronously in JSX - no yield needed!
  view.add(
    <Img src={cached('https://example.com/image.png')} />
  );

  view.add(
    <Audio src={cached('https://example.com/audio.mp3')} />
  );
});

Alternative: Preload in variables first

export default makeScene2D(function* (view) {
  // Preload and store in variables
  const imageUrl = yield cache('https://example.com/image.png');
  const audioUrl = yield cache('https://example.com/audio.mp3');

  // Use directly in JSX
  view.add(<Img src={imageUrl} />);
  view.add(<Audio src={audioUrl} />);
});

3. Conditional Loading

If you need to check if something is cached before loading:

export default makeScene2D(function* (view) {
  const url = 'https://example.com/image.png';

  // Check if already cached (synchronous)
  if (!cached(url)) {
    // Not cached yet, load it
    yield cache(url);
  }

  // Now use it in JSX
  view.add(<Img src={cached(url)} />);
});

Advanced Usage

Cache with custom MIME type override

export default makeScene2D(function* (view) {
  // Preload with custom MIME type
  yield cache('https://example.com/file', {
    mimeType: 'audio/mpeg', // Override auto-detected MIME type
    metadata: {duration: 5.2, title: 'My Audio'},
  });

  // Use in JSX
  view.add(<Audio src={cached('https://example.com/file')} />);
});

Cache a Blob directly

import {Cache, CacheUtils, cached} from 'motion-canvas-cache';

export default makeScene2D(function* (view) {
  const cache = Cache.getInstance();
  const myBlob = new Blob(['content'], {type: 'text/plain'});

  // Generate a cache key
  const cacheKey = CacheUtils.generateCacheKey('my-content', ['text']);

  // Cache the blob (use yield* in Motion Canvas)
  yield* cache.cacheBlob(cacheKey, myBlob, {
    source: 'user-generated',
  });

  // Use the cached result
  const textUrl = cache.get(cacheKey).url;
});

Custom cache key options

export default makeScene2D(function* (view) {
  const baseUrl = 'https://api.example.com/audio';

  // Preload different variants
  yield cache(baseUrl, {
    cacheKeyOptions: ['fast', 'lowquality'],
    metadata: {speed: 'fast'},
  });

  yield cache(baseUrl, {
    cacheKeyOptions: ['slow', 'highquality'],
    metadata: {speed: 'slow'},
  });

  // Use them
  const fastUrl = cached(baseUrl, {cacheKeyOptions: ['fast', 'lowquality']});
  const slowUrl = cached(baseUrl, {cacheKeyOptions: ['slow', 'highquality']});
});

Override cache key completely

export default makeScene2D(function* (view) {
  // Cache with custom key
  yield cache('https://api.tts.com/generate?text=hello', {
    cacheKey: 'my-custom-cache-key',
    metadata: {duration: 2.5},
  });

  // Retrieve with same custom key
  const audioUrl = cached('https://api.tts.com/generate?text=hello', {
    cacheKey: 'my-custom-cache-key',
  });

  // Note: If both cacheKey and cacheKeyOptions are provided,
  // cacheKeyOptions will be ignored and a warning will be logged
});

Use URL or Request objects

export default makeScene2D(function* (view) {
  // With URL object
  const url = new URL('https://example.com/image.png');
  yield cache(url);
  view.add(<Img src={cached(url)} />);

  // With Request object (for POST requests, custom headers, etc.)
  const request = new Request('https://api.tts.com/generate', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({text: 'Hello world'}),
  });

  yield cache(request, {
    cacheKey: 'hello-world-audio',
    metadata: {duration: 2.5},
  });

  const audioUrl = cached(request, {cacheKey: 'hello-world-audio'});
});

Custom fetch options

export default makeScene2D(function* (view) {
  // Preload with fetch options
  yield cache('https://api.tts.com/generate', {
    fetchOptions: {
      method: 'POST',
      headers: {'Authorization': 'Bearer token123'},
      body: JSON.stringify({text: 'Hello'}),
    },
    metadata: {duration: 3.5},
  });

  // Use the cached result
  const audioUrl = cached('https://api.tts.com/generate');
  view.add(<Audio src={audioUrl} />);
});

How It Works

  1. First call: When you call cache() with a URL:

    • Checks in-memory cache first
    • Checks server cache via HMR WebSocket
    • If not cached, fetches from URL
    • Stores in both memory and server cache
    • Returns the cached URL (blob URL or server path)
  2. Subsequent calls: Same cache key returns instantly from memory or server cache

  3. HMR updates: Cache persists across hot module reloads for fast development

API Reference

cache(input, options?)

Async function to fetch and cache URLs. Use with yield in Motion Canvas.

Parameters:

  • input: string | URL | Request - URL to fetch and cache
    • string - URL as string
    • URL - URL object
    • Request - Request object (for POST, custom headers, etc.)
  • options: CachedOptions (optional)
    • metadata?: Record<string, any> - Custom metadata to store
    • mimeType?: string - Override MIME type (auto-detected if not provided)
    • cacheKeyOptions?: string[] - Additional options for cache key generation
    • cacheKey?: string - Override the automatically generated cache key (if provided, cacheKeyOptions will be ignored)
    • fetchOptions?: RequestInit - Additional options to pass to fetch() (method, headers, body, etc.)

Returns: Promise<string> - The cached URL (blob URL or server path) ready to use in Motion Canvas components

Usage: const url = yield cache('https://...');

cached(input, options?)

Synchronously get a cached URL. No yield needed - use directly in JSX!

Parameters:

  • Same as cache()

Returns: string | null - The cached URL if found, otherwise null

Usage: const url = cached('https://...'); // No yield needed!

Cache class

Singleton cache manager.

Methods:

  • Cache.getInstance() - Get the singleton instance
  • get(cacheKey) - Get cached result
  • cacheResult(cacheKey, url, metadata?) - Store URL in memory cache
  • cacheBlob(cacheKey, blob, metadata?, mimeType?) - Cache a Blob with optional metadata and MIME type
  • checkServerCache(cacheKey) - Check server cache
  • uploadToServer(cacheKey, data, mimeType, metadata?) - Upload to server

CacheUtils class

Utility functions for cache operations.

Methods:

  • CacheUtils.generateCacheKey(content, opts?) - Generate 8-character hex cache key
  • CacheUtils.blobToDataUrl(blob) - Convert Blob to base64 data URL
  • CacheUtils.streamToArrayBuffer(stream) - Convert ReadableStream to ArrayBuffer

Integration with Motion Canvas Narrator

This library was extracted from the motion-canvas-narrator project and can be reused there. Example integration:

// In your narrator provider (generator function)
import {cache, cached} from 'motion-canvas-cache';

function* generateAudio(text: string, voiceId: string, modelId: string) {
  const url = 'https://api.example.com/tts';

  // Check if already cached
  const cachedUrl = cached(url, {
    cacheKeyOptions: [text, voiceId, modelId],
  });

  if (cachedUrl) {
    return cachedUrl;
  }

  // Cache with audio duration metadata and return the cached URL
  return yield cache(url, {
    fetchOptions: {
      method: 'POST',
      body: JSON.stringify({text, voice: voiceId}),
    },
    metadata: {duration: 3.5},
    cacheKeyOptions: [text, voiceId, modelId], // Unique per text/voice/model
  });
}

Cache Directory Structure

Server-side cached files are stored as:

motion-canvas-cache/
├── 20fac170.mp3           # Cached file (8-char hash + extension)
├── 20fac170.meta.json     # Metadata file
├── a1b2c3d4.png
├── a1b2c3d4.meta.json
└── ...

Metadata file example:

{
  "cacheKey": "20fac170",
  "mimeType": "audio/mpeg",
  "fileSize": 55296,
  "fileName": "20fac170.mp3",
  "createdAt": "2024-01-01T12:00:00.000Z",
  "duration": 3.5
}

Cleanup

To clear the server cache, visit:

http://localhost:9000/__cache-cleanup

Contributing

Contributions are welcome! Please open issues or pull requests.

License

MIT