Welcome to Pixelpop! This guide will help you get up and running with displaying beautiful images and animations in your terminal applications.
- Node.js 20+ - Pixelpop requires a modern Node.js version
- ESM-only package - Use ESM in your project (or dynamic
import()from CJS) - Terminal - Any terminal that supports ANSI colors (most do!)
Choose your preferred package manager:
# NPM
npm install @pinkpixel/pixelpop
# Yarn
yarn add @pinkpixel/pixelpop
# PNPM
pnpm add @pinkpixel/pixelpopLet's start with the simplest possible example:
import pixelPop from "@pinkpixel/pixelpop";
// Display an image file
const output = await pixelPop.file("./my-image.jpg");
console.log(output);That's it! Pixelpop will automatically:
- Detect your terminal's capabilities
- Choose the best rendering method
- Size the image to fit your terminal
- Display it with optimal quality
Pixelpop uses three different rendering strategies depending on your terminal:
-
🏆 Native Support (Best quality)
- Works in: iTerm2, Hyper
- Uses terminal's built-in image protocols
-
⚡ Kitty Protocol (High quality)
- Works in: Kitty, WezTerm, Konsole
- Direct image rendering with excellent quality
-
🌈 ANSI Fallback (Universal)
- Works in: Any terminal with ANSI color support
- Block character rendering with RGB colors
Pixelpop automatically detects your terminal by checking environment variables:
// These are checked automatically - no configuration needed!
process.env.TERM_PROGRAM; // 'iTerm.app', 'WezTerm', etc.
process.env.TERM; // 'xterm-kitty', etc.
process.env.KITTY_WINDOW_ID; // Present in Kitty terminalPerfect for responsive terminal applications:
// Fill 80% of terminal width
await pixelPop.file("./image.jpg", { width: "80%" });
// Fill 50% of terminal height
await pixelPop.file("./image.jpg", { height: "50%" });
// Both dimensions
await pixelPop.file("./image.jpg", {
width: "80%",
height: "60%",
});For precise control:
// Exact pixel dimensions
await pixelPop.file("./image.jpg", {
width: 120,
height: 80,
});// Maintain original proportions (default)
await pixelPop.file("./image.jpg", {
width: "80%",
preserveAspectRatio: true, // default
});
// Stretch to fit exact dimensions
await pixelPop.file("./image.jpg", {
width: 100,
height: 100,
preserveAspectRatio: false,
});Animated GIFs are just as easy:
// Start animation
const stop = await pixelPop.gifFile("./animation.gif", {
width: "60%",
});
// Stop after 5 seconds
setTimeout(stop, 5000);Control animation smoothness and performance:
// High-quality smooth animation
const stop = await pixelPop.gifFile("./smooth-animation.gif", {
maximumFrameRate: 30, // Balanced quality and performance
});
// Battery-friendly animation
const stopSlow = await pixelPop.gifFile("./efficient-animation.gif", {
maximumFrameRate: 15, // Lower CPU usage
});
// Ultra-smooth for high-end terminals
const stopUltra = await pixelPop.gifFile("./premium-animation.gif", {
maximumFrameRate: 60, // Maximum smoothness (iTerm2, Kitty)
});Sometimes you have image data in memory instead of files:
import { readFile } from "fs/promises";
// Read image into buffer
const imageBuffer = await readFile("./photo.jpg");
// Display the buffer
await pixelPop.buffer(imageBuffer, {
width: "50%",
});
// Same for GIFs
const gifBuffer = await readFile("./animation.gif");
const stop = await pixelPop.gifBuffer(gifBuffer, {
maximumFrameRate: 24,
});Perfect for command-line utilities:
#!/usr/bin/env node
import pixelPop from "@pinkpixel/pixelpop";
const [, , imagePath] = process.argv;
if (!imagePath) {
console.error("Usage: my-tool <image-path>");
process.exit(1);
}
try {
await pixelPop.file(imagePath, {
width: "80%",
preserveAspectRatio: true,
});
} catch (error) {
console.error("Failed to display image:", error.message);
process.exit(1);
}Use GIFs for loading animations:
// Start loading animation
const stopLoading = await pixelPop.gifFile("./spinner.gif", {
width: "10%",
});
// Do some work
await performLongOperation();
// Stop the loading animation
stopLoading();
console.log("✅ Operation complete!");Display multiple images in sequence:
const images = ["./photo1.jpg", "./photo2.jpg", "./photo3.jpg"];
for (const imagePath of images) {
console.log(`\n📷 Showing: ${imagePath}`);
await pixelPop.file(imagePath, { width: "80%" });
// Wait for user input
await new Promise((resolve) => {
process.stdin.once("data", resolve);
console.log("Press any key for next image...");
});
}- ✨ Best image quality with native rendering
- 🖼️ Supports largest images without performance issues
- ⚙️ No setup required - works out of the box
- 🎬 Excellent GIF support with high frame rates
- ⚡ Excellent quality with Kitty graphics protocol
- 🚀 Fast rendering performance across platforms
- 🌈 Great transparency support for PNG images
- 💻 Cross-platform consistency
- 💻 Optimized ANSI rendering for integrated terminal
- 🔧 Special handling for transparency and colors
- 📄 Works well with development workflows
- 💻 Modern Windows support with good color reproduction
- ⚙️ Optimized character rendering for Windows fonts
- 🌈 Decent color support in ANSI mode
- 🌈 Universal ANSI fallback works everywhere
- 📉 Smaller images recommended for better readability
- ⚡ Lower frame rates reduce terminal flicker
Always handle errors gracefully:
async function displayImageSafely(path: string) {
try {
await pixelPop.file(path, { width: "80%" });
} catch (error) {
if (error.code === "ENOENT") {
console.error(`❌ Image not found: ${path}`);
} else if (error.message.includes("too small")) {
console.error("❌ Image is too small to render");
} else {
console.error("❌ Failed to display image:", error.message);
}
}
}For advanced GIF control:
import logUpdate from "log-update";
const customRenderer = (frame: string) => {
// Custom frame display logic
logUpdate(`🎬 Custom Animation:\n${frame}`);
};
customRenderer.done = () => {
logUpdate.done();
console.log("🎉 Animation finished!");
};
await pixelPop.gifFile("./animation.gif", {
renderFrame: customRenderer,
maximumFrameRate: 20,
});- Use appropriate image sizes - Don't load huge images if you're displaying them small
- Control GIF frame rates - Lower frame rates use less CPU
- Cache processed images - If displaying the same image multiple times
- Use buffers efficiently - Reuse buffers when possible
// Good: Reasonable frame rate
await pixelPop.gifFile("./animation.gif", {
maximumFrameRate: 24, // Smooth but not excessive
});
// Less optimal: Very high frame rate
await pixelPop.gifFile("./animation.gif", {
maximumFrameRate: 120, // May be overkill and CPU-intensive
});Not sure what rendering method is being used? Check your environment:
console.log("Terminal:", process.env.TERM_PROGRAM);
console.log("Term type:", process.env.TERM);
console.log("Kitty window:", process.env.KITTY_WINDOW_ID ? "Yes" : "No");For troubleshooting, you can add logging:
try {
console.log("Attempting to display image...");
const output = await pixelPop.file("./test.jpg", {
width: "50%",
});
console.log("Success! Output length:", output.length);
} catch (error) {
console.error("Display failed:", error);
}Now that you're comfortable with the basics:
- Explore the API Reference for complete method documentation
- Check out Examples & Recipes for more advanced patterns
- Read the GIF Animation Guide for animation techniques
- Learn about Terminal Compatibility for optimal results
// Static images
await pixelPop.file("./image.jpg", { width: "80%" });
await pixelPop.buffer(imageBuffer, { height: 40 });
// Animated GIFs
const stop = await pixelPop.gifFile("./anim.gif", {
width: "50%",
maximumFrameRate: 30,
});
// Clean up
setTimeout(stop, 10000);Happy coding! 🎉
Made with ❤️ by Pink Pixel
"Dream it, Pixel it" ✨