ImageScript
ImageScript is a zero-dependency JavaScript library designed for high-performance bitmap image manipulation. It supports a wide array of image formats, including decoding PNG, JPEG, TIFF, and GIF, along with capabilities for rendering SVGs and vector fonts. The library also handles encoding images to PNG, JPEG, WebP, and GIF. Currently at version 1.4.0, it distinguishes itself from other JavaScript imaging tools by leveraging lower-level memory access, reduced memory copying, and utilizing WebAssembly or native binaries for superior performance and a smaller memory footprint. It offers a comprehensive set of image manipulation functions like cropping, rotation, compositing, and various color adjustments. The project maintains an active development pace with regular updates and ships with TypeScript types, enhancing developer experience.
Common errors
-
TypeError: Image.decode is not a function
cause This error typically occurs if `Image.decode` was called without `await` in an async context, returning a Promise instead of the Image class itself, or if the `Image` class was not correctly imported.fixEnsure `import { Image } from 'imagescript';` is used and the call is `const image = await Image.decode(buffer);` within an `async` function. For CommonJS, use `const { Image } = require('imagescript');`. -
Error: Invalid image data
cause The provided buffer does not contain valid image data for any of the supported formats, or the data is corrupted/incomplete.fixVerify that the input buffer is a complete and uncorrupted image file of a supported type (PNG, JPEG, TIFF, GIF) and that the file path or data source is correct. -
ERR_REQUIRE_ESM
cause This error arises when a CommonJS module (`require`) attempts to import an ESM-only package or when Node.js is configured for ESM but a CommonJS-style `require` is used with a mixed module type.fixFor ImageScript, prefer ESM imports (`import { ... } from 'imagescript';`). If you must use CommonJS, ensure your project setup allows for interoperability or stick to `require` for the entire codebase if it's a CJS project. Set `"type": "module"` in `package.json` for ESM projects or use `.mjs` extensions.
Warnings
- gotcha All image decoding and encoding operations (e.g., `Image.decode`, `Image.encode`, `GIF.decode`, `GIF.encode`) are asynchronous and return Promises. Forgetting to use `await` will result in unexpected behavior, typically a `TypeError` when attempting to call methods on the Promise object instead of the resolved Image or GIF instance.
- gotcha While ImageScript is zero-dependency and performance-focused, handling very large images can still consume significant memory and CPU resources. Users processing high-resolution images in constrained environments should monitor memory usage and consider optimizing workflows (e.g., processing smaller tiles).
- gotcha ImageScript relies on specific byte patterns and structures for image decoding. Providing corrupted or malformed image data (e.g., an incomplete buffer, a file with an incorrect extension, or an unsupported sub-format) will lead to decoding errors, preventing image processing.
Install
-
npm install imagescript -
yarn add imagescript -
pnpm add imagescript
Imports
- Image
const { Image } = require('imagescript');import { Image } from 'imagescript'; - GIF
const { GIF } = require('imagescript');import { GIF } from 'imagescript'; - Image.decode
const { Image } = require('imagescript'); const image = Image.decode(buffer);import { Image } from 'imagescript'; const image = await Image.decode(buffer);
Quickstart
import { Image, GIF } from 'imagescript';
import { readFileSync, writeFileSync } from 'fs';
async function processImage() {
// Load an image from a buffer
const inputBuffer = readFileSync('./assets/example.png'); // Replace with a valid path to a PNG image
const image = await Image.decode(inputBuffer);
// Resize and add a red tint
image.resize(200, Image.RESIZE_AUTO);
image.tint(255, 0, 0, 0.2); // 20% red tint
// Composite another image (e.g., a watermark) if available
try {
const watermarkBuffer = readFileSync('./assets/watermark.png'); // Optional: replace with a watermark image
const watermark = await Image.decode(watermarkBuffer);
watermark.resize(image.width / 4, Image.RESIZE_AUTO);
image.composite(watermark, image.width - watermark.width - 10, image.height - watermark.height - 10);
} catch (e) {
console.warn('Watermark image not found, skipping compositing.');
}
// Render text onto the image
image.renderText(
'Hello ImageScript!',
'sans-serif', // Font family (must be available in the environment)
48,
0, 0, // position
0xFFFFFFFF, // white color with full alpha
0x000000FF, // black outline with full alpha
2
);
// Encode the modified image to a new PNG file
const outputBuffer = await image.encode();
writeFileSync('./output_image.png', outputBuffer);
console.log('Image processed and saved to output_image.png');
// Example of GIF decoding/encoding (requires a GIF file)
try {
const gifBuffer = readFileSync('./assets/example.gif'); // Replace with a valid path to a GIF image
const gif = await GIF.decode(gifBuffer);
gif.loop = 0; // Loop indefinitely
gif.frames.forEach(frame => {
frame.image.rotate(10); // Rotate each frame by 10 degrees
});
const outputGifBuffer = await gif.encode();
writeFileSync('./output_gif.gif', outputGifBuffer);
console.log('GIF processed and saved to output_gif.gif');
} catch (e) {
console.warn('GIF example skipped: Make sure to have a ./assets/example.gif file.');
}
}
// Create a dummy image file for the example if it doesn't exist
try {
readFileSync('./assets/example.png');
} catch (e) {
console.log('Creating dummy assets for quickstart...');
const dummyImage = new Image(300, 200);
dummyImage.fill(0x0000FFFF); // Blue background
dummyImage.encode().then(buffer => writeFileSync('./assets/example.png', buffer));
const dummyWatermark = new Image(100, 50);
dummyWatermark.fill(0xFF000088); // Semi-transparent red
dummyWatermark.encode().then(buffer => writeFileSync('./assets/watermark.png', buffer));
console.log('Dummy images created. Re-run quickstart after this.');
// Exit so the user can re-run with the created files
process.exit(0);
}
processImage().catch(console.error);