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

@shopg41/wimp

v0.0.2

Published

wimp - Web-based Image Manipulation Program

Readme

wimp - Web-based Image Manipulation Program

I created this project because I couldn't find an open-source and free-to-use image editor library that had solid touch support and was actively maintained. My goal is to provide a powerful and easy-to-use image editor for developers to integrate into their projects.

This project is a professional image editing application built with React, HTML5 Canvas, and Zustand for state management.

Features

  • Transform Tools: Rotate, flip, and resize your image with ease.
  • Crop Tools: Crop your images with various aspect ratio guides or freeform.
  • Drawing Tools: Draw on your images with a pencil, eraser, and text tool.
  • Adjustments: Brightness and contrast controls.
  • Filters: Apply grayscale, sepia, and vintage filters.
  • Secure Blur: Redact sensitive information with blur tools.
  • File Operations: Open, save, and export images in multiple formats.
  • Undo/Redo: Don't worry about making mistakes, you can always undo and redo your actions.
  • Touch Support: Works on mobile devices with touch support.
  • Zoom & Pan: Navigate large images with intuitive controls.

Architecture

This project uses:

  • React - UI framework
  • Zustand - State management with a centralized store
  • TypeBox - Runtime type validation
  • HTML5 Canvas - Image manipulation

Store Structure

The Zustand store (src/store/canvasStore.ts) manages all application state:

  • Tool State: Current tool selection
  • Drawing Settings: Color, brush size, font family
  • Text Objects: Text overlays on canvas
  • Adjustments: Brightness, contrast, filters
  • Blur Settings: Radius and mode
  • Crop State: Crop mode, selection, aspect ratios
  • Zoom/Pan: Viewport transformations
  • History: Undo/redo stack (limited to 20 states)

Components access state directly from the store using useCanvasStore(), eliminating prop drilling.

Getting Started

  1. Clone the repository:
    git clone https://github.com/shopg41/wimp.git
  2. Install dependencies:
    bun install
  3. Start the development server:
    bun run dev

Installation

npm install @shopg41/wimp
# or
bun add @shopg41/wimp
# or
yarn add @shopg41/wimp

Using it as a Library

To use this image editor in your own project, import the Wimp component and the necessary styles.

import { Wimp } from '@shopg41/wimp';
import '@shopg41/wimp/style.css';

function App() {
  return (
    <div style={{ height: '100vh' }}>
      <Wimp />
    </div>
  );
}

Accessing the Store

You can also access the Zustand store directly:

import { useCanvasStore } from '@shopg41/wimp';

function MyComponent() {
  const currentTool = useCanvasStore((state) => state.currentTool);
  const setCurrentTool = useCanvasStore((state) => state.setCurrentTool);
  const zoom = useCanvasStore((state) => state.zoom);
  
  // Use selectors for better performance
  const historyIndex = useCanvasStore((s) => s.historyIndex);
  
  return (
    <div>
      <p>Current tool: {currentTool}</p>
      <p>Zoom: {Math.round(zoom * 100)}%</p>
      <p>History: {historyIndex}</p>
    </div>
  );
}

Performance Benchmarks

Performance benchmarks are run using Bun. Results below are from local development environment (M2 MacBook).

Canvas Scaling Performance

Time to scale images using HTML5 Canvas drawImage with high-quality smoothing.

| Image Size | Scale 0.5x | Scale 1.0x | Scale 1.5x | Scale 2.0x | |------------|------------|------------|------------|------------| | Small (640x480) | 1.38ms | 3.81ms | 8.37ms | 14.52ms | | Medium (1280x720) | 2.70ms | 10.71ms | 24.99ms | 43.86ms | | Large (1920x1080) | 6.19ms | 24.14ms | 55.05ms | 99.00ms | | 4K (3840x2160) | 24.55ms | 98.24ms | 227.44ms | 415.45ms |

Scaling Performance Notes:

  • Operations scale quadratically with image dimensions
  • 4K image scaling to 2x can take 400+ ms (notable UI lag)
  • Recommendation: Show loading indicator for images >2K resolution

Blur Processing Performance

Time to apply secure blur with different radii (optimized algorithm).

| Image Size | Radius 10px | Radius 25px | Radius 50px | |------------|-------------|-------------|--------------| | Medium (400x300) | 21ms | 88ms | 373ms | | Large (800x600) | 59ms | 349ms | 1493ms | | Full HD (1920x1080) | 192ms | 1138ms | 4886ms |

Blur Performance Notes:

  • Selective blur is 40% faster at 25px radius vs original implementation
  • For full-image blur (no mask), uses stack blur algorithm - O(n) complexity
  • Stack blur stays ~50ms for 400x300 regardless of radius
  • Large images with high radius still block - consider Web Worker for production

Full Blur Performance (Stack Blur)

When blurring entire image (no selective mask):

| Image Size | Radius 10px | Radius 25px | Radius 50px | |------------|-------------|-------------|--------------| | Medium (400x300) | 8ms | 18ms | 52ms | | Large (800x600) | 28ms | 65ms | 204ms | | Full HD (1920x1080) | 704ms | 863ms | 875ms |

Full Blur Notes:

  • Stack blur algorithm is O(n) - scales linearly with image size
  • Radius has minimal impact on performance (constant time per pixel)
  • Recommended for real-time preview when blurring full image

Canvas Cropping Performance

Time to crop images at various percentages.

| Image Size | Crop 25% | Crop 50% | Crop 75% | |------------|----------|----------|----------| | Small (640x480) | 3.63ms | 6.30ms | 11.31ms | | Medium (1280x720) | 10.41ms | 20.41ms | 33.40ms | | Large (1920x1080) | 21.64ms | 41.22ms | 76.98ms | | 4K (3840x2160) | 86.82ms | 167.40ms | 324.76ms |

Cropping Performance Notes:

  • Cropping involves getImageData, temp canvas creation, and putImageData
  • Large crops take longer due to memory operations
  • 4K crops can take 80-325ms depending on crop size

Canvas History Performance

Time to save/restore canvas state for undo/redo functionality.

| Image Size | Save State | Undo Operation | |------------|------------|----------------| | Small (640x480) | 16.75ms | 2.73ms | | Medium (1280x720) | 49.97ms | 7.50ms | | Large (1920x1080) | 112.69ms | 18.21ms | | 4K (3840x2160) | 430.22ms | 74.84ms |

History Performance Notes:

  • Save state is memory-intensive (stores full ImageData)
  • 4K images use ~32MB per history state
  • History is limited to 20 states to prevent memory issues
  • Undo is faster than save (no memory allocation)

Interaction Performance

| Operation | Average Time | |-----------|--------------| | Resize Handle Detection | 0.019ms | | Coordinate Bounding | 0.002ms |

Interaction Notes:

  • UI interactions are extremely fast (<0.02ms)
  • No performance concerns for real-time interactions

Throughput Test

Continuous operations per second for 1 second:

| Resolution | Operations/sec | Throughput | |------------|----------------|------------| | 720p (1280x720) | 76.5 | 70.5 MP/s | | 1080p (1920x1080) | 37.8 | 78.5 MP/s | | 4K (3840x2160) | 9.9 | 81.9 MP/s |

Throughput Notes:

  • Sustained throughput of ~80 MP/s
  • Performance is consistent across resolutions

File Decoding Performance

Time to decode images from file (FileReader + Image element).

| Image Size | JPEG Read | PNG Read | JPEG Decode | PNG Decode | |------------|-----------|----------|-------------|------------| | Small (640x480) | 0.034ms | 0.210ms | <0.001ms | <0.001ms | | Medium (1280x720) | 0.302ms | 0.841ms | <0.001ms | <0.001ms | | Large (1920x1080) | 0.047ms | 1.172ms | <0.001ms | <0.001ms | | 4K (3840x2160) | 0.397ms | 7.044ms | <0.001ms | <0.001ms |

Decoding Performance Notes:

  • PNG files take longer to read due to larger file size
  • JPEG decoding is extremely fast (browser optimized)
  • Full load pipeline (File → Decoded): <1ms for all sizes in benchmarks
  • Actual browser performance may vary based on image complexity

Running Benchmarks

# Run all performance tests
bun run benchmarks/performance-test.ts

# Run decode performance tests
bun run benchmarks/decode-performance.ts

# Run unit tests
bun test

API Reference

Store Exports

The following types and functions are exported from the store:

// Types
import { 
  Tool,           // "pencil" | "eraser" | "blur" | "text" | "crop"
  CropState,      // "inactive" | "creating" | "set" | "moving" | "resizing"
  Filter,        // "none" | "grayscale" | "sepia" | "vintage"
  BlurMode,      // "marked" | "unmarked"
  TextObject,
  Point,
  CropRect,
} from '@shopg41/wimp';

// Store
import { useCanvasStore } from '@shopg41/wimp';

Store Actions

const store = useCanvasStore.getState();

// Tool
store.setCurrentTool('pencil');

// Drawing
store.setDrawingColor('#ff0000');
store.setBrushSize(10);
store.setFontFamily('Arial');

// Text
store.addTextObject({ id: '1', text: 'Hello', x: 100, y: 100, fontSize: 24, fontFamily: 'Arial', color: '#000' });
store.updateTextObject('1', { text: 'Updated' });
store.removeTextObject('1');

// Adjustments
store.setBrightness(120);
store.setContrast(110);
store.setCurrentFilter('sepia');

// Blur
store.setBlurRadius(25);
store.setBlurMode('marked');

// Crop
store.setCropMode(true);
store.setCropGuide('16:9');
store.resetCrop();

// Zoom/Pan
store.setZoom(2);
store.setPan({ x: 100, y: 50 });
store.zoomIn();
store.zoomOut();
store.autoFit();

// History
store.saveCanvasState();
store.undo();
store.redo();
store.clearHistory();

// File operations
store.loadImageUrl('https://example.com/image.jpg');
store.exportImage('png', 0.92, 1);

Subscribing to Store Changes

// Subscribe to specific state changes
useEffect(() => {
  return useCanvasStore.subscribe(
    (state) => state.currentTool,
    (tool) => console.log('Tool changed:', tool)
  );
}, []);

// Get current state
const state = useCanvasStore.getState();

License

This project is licensed under the MIT License. See the LICENSE file for details.