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

@uitgeteld/grainular

v1.0.0

Published

Professional film grain effects and image processing for Node.js. Perfect for Discord bots, automation, and server-side image manipulation.

Downloads

4

Readme

Grainular

Professional film grain effects and image processing for Node.js. Perfect for Discord bots, automation, and server-side image manipulation.

npm version license

Installation

npm install grainular

Features

  • 🎞️ Film Grain Effects - Add realistic colored or black & white grain
  • Image Adjustments - Brightness, saturation, and B&W conversion
  • 🤖 Discord Bot Ready - Works seamlessly with Canvas/Sharp
  • 🚀 Fast & Lightweight - Pure JavaScript, no heavy dependencies
  • 💻 Node.js Native - Designed for server-side processing

Quick Start

const { addGrain, saturate, adjustBrightness, toBlackWhite } = require('grainular');

// Your ImageData object from Canvas or similar
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

// Apply effects
imageData = addGrain(imageData, 30, true);        // Add colored grain (30% intensity)
imageData = saturate(imageData, 1.5);             // Increase saturation (150%)
imageData = adjustBrightness(imageData, 1.2);     // Increase brightness (120%)
imageData = toBlackWhite(imageData);              // Convert to black & white

// Put back to canvas
ctx.putImageData(imageData, 0, 0);

API Reference

addGrain(imageData, intensity, colored)

Adds film grain effect to an image.

Parameters:

  • imageData (ImageData) - Canvas ImageData object containing pixel data
  • intensity (Number) - Grain intensity from 0-100 (default: 20)
  • colored (Boolean) - true for colored grain, false for monochrome (default: false)

Returns: Modified ImageData object

Example:

// Add subtle colored grain
imageData = addGrain(imageData, 15, true);

// Add strong B&W grain
imageData = addGrain(imageData, 50, false);

saturate(imageData, factor)

Adjusts image saturation.

Parameters:

  • imageData (ImageData) - Canvas ImageData object containing pixel data
  • factor (Number) - Saturation multiplier (0 = grayscale, 1 = original, >1 = more saturated)

Returns: Modified ImageData object

Example:

// Increase saturation by 50%
imageData = saturate(imageData, 1.5);

// Reduce saturation by half
imageData = saturate(imageData, 0.5);

// Full grayscale
imageData = saturate(imageData, 0);

adjustBrightness(imageData, factor)

Adjusts image brightness.

Parameters:

  • imageData (ImageData) - Canvas ImageData object containing pixel data
  • factor (Number) - Brightness multiplier (0 = black, 1 = original, >1 = brighter)

Returns: Modified ImageData object

Example:

// Increase brightness by 20%
imageData = adjustBrightness(imageData, 1.2);

// Darken by 30%
imageData = adjustBrightness(imageData, 0.7);

toBlackWhite(imageData)

Converts image to black and white using luminance formula.

Parameters:

  • imageData (ImageData) - Canvas ImageData object containing pixel data

Returns: Modified ImageData object

Example:

imageData = toBlackWhite(imageData);

Usage Examples

Discord Bot with Canvas

const { createCanvas, loadImage } = require('canvas');
const { addGrain, saturate } = require('grainular');
const { AttachmentBuilder } = require('discord.js');

client.on('messageCreate', async (message) => {
  if (message.content === '!grain' && message.attachments.size > 0) {
    const attachment = message.attachments.first();
    
    // Load image
    const image = await loadImage(attachment.url);
    const canvas = createCanvas(image.width, image.height);
    const ctx = canvas.getContext('2d');
    
    // Draw and get image data
    ctx.drawImage(image, 0, 0);
    let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    
    // Apply effects
    imageData = addGrain(imageData, 40, true);
    imageData = saturate(imageData, 1.3);
    
    // Put back and send
    ctx.putImageData(imageData, 0, 0);
    const buffer = canvas.toBuffer('image/png');
    const attachment = new AttachmentBuilder(buffer, { name: 'grainy.png' });
    
    await message.reply({ files: [attachment] });
  }
});

Processing Multiple Images

const fs = require('fs');
const { createCanvas, loadImage } = require('canvas');
const { addGrain, toBlackWhite } = require('grainular');

async function processImage(inputPath, outputPath) {
  const image = await loadImage(inputPath);
  const canvas = createCanvas(image.width, image.height);
  const ctx = canvas.getContext('2d');
  
  ctx.drawImage(image, 0, 0);
  let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  
  // Apply vintage film look
  imageData = toBlackWhite(imageData);
  imageData = addGrain(imageData, 35, false);
  
  ctx.putImageData(imageData, 0, 0);
  const buffer = canvas.toBuffer('image/png');
  fs.writeFileSync(outputPath, buffer);
}

// Process batch
const images = ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'];
for (const img of images) {
  await processImage(img, `processed_${img}`);
}

Custom Film Presets

const { addGrain, saturate, adjustBrightness, toBlackWhite } = require('grainular');

function vintageFilm(imageData) {
  imageData = toBlackWhite(imageData);
  imageData = addGrain(imageData, 45, false);
  imageData = adjustBrightness(imageData, 0.9);
  return imageData;
}

function colorPop(imageData) {
  imageData = saturate(imageData, 1.8);
  imageData = addGrain(imageData, 20, true);
  imageData = adjustBrightness(imageData, 1.1);
  return imageData;
}

function softGlow(imageData) {
  imageData = adjustBrightness(imageData, 1.15);
  imageData = saturate(imageData, 0.8);
  imageData = addGrain(imageData, 10, true);
  return imageData;
}

// Use presets
imageData = vintageFilm(imageData);

Important Notes

  • ImageData Format: All functions expect and return Canvas ImageData objects
  • Non-Destructive: Original imageData is not modified; a new object is returned
  • Chaining: Functions can be chained for complex effects
  • Performance: Processing time scales with image resolution
  • Color Space: Works with RGBA color space (8-bit per channel)

Requirements

  • Node.js 12.x or higher
  • A Canvas implementation (e.g., canvas package) for ImageData objects

Web Version

Looking for the browser-based editor? Check out the Grainular web app.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Contact

License

See LICENSE file for details.


Built with ❤️ for developers and creators.