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

@patrickisgreat/string-art-engine

v1.0.0

Published

Framework-agnostic string art generation engine using WebAssembly

Readme

String Art Engine

A high-performance, framework-agnostic string art generation engine powered by WebAssembly and Rust. Transform any image into beautiful string art with advanced algorithms and customizable parameters.

Features

  • High Performance: WebAssembly-powered Rust algorithms for fast processing
  • Framework Agnostic: Works with React, Vue, Angular, vanilla JS, or any TypeScript/JavaScript project
  • Highly Configurable: Extensive parameters for fine-tuning results
  • Advanced Algorithms: Smart preprocessing, quality analysis, and optimization
  • Parallel Processing: Multi-threaded processing for complex images
  • Precise Control: Line count optimization and thread thickness simulation
  • Quality Analysis: Automatic image suitability assessment

Installation

npm install string-art-engine

Quick Start

import { StringArtEngine } from 'string-art-engine';

// Create engine instance
const engine = new StringArtEngine({
  pinCount: 200,
  maxLineCount: 4000,
  lineWeight: 0.25
});

// Process an image
async function generateStringArt(imageData: ImageData) {
  const result = await engine.processImage(imageData);
  
  console.log(`Generated ${result.lineCount} lines`);
  console.log('Pin coordinates:', result.pinCoordinates);
  console.log('Line sequence:', result.lineSequence);
  
  return result;
}

Advanced Usage

Custom Configuration

import { StringArtEngine, DEFAULT_CONFIG } from 'string-art-engine';

const engine = new StringArtEngine({
  ...DEFAULT_CONFIG,
  pinCount: 300,           // More pins = finer detail
  maxLineCount: 6000,      // More lines = more detail
  lightnessPenalty: 0.8,   // Higher = more emphasis on dark areas
  qualityMode: 'enhanced', // Better quality at cost of speed
  useParallelProcessing: true // Use multiple threads
}, {
  enableLogging: true,     // Enable console logging
  useWorker: true,         // Use web worker (recommended)
  onProgress: (progress) => {
    console.log(`${progress.stage}: ${progress.progress}%`);
  }
});

Image Quality Analysis

// Analyze image before processing
const analysis = await engine.getQualityAnalysis(imageData);
console.log(`Quality Score: ${(analysis.score * 100).toFixed(1)}%`);
console.log(`Recommendation: ${analysis.recommendation}`);

// Preprocess low-quality images
if (analysis.score < 0.5) {
  const preprocessed = await engine.preprocessImage(imageData);
  if (preprocessed) {
    imageData = preprocessed;
  }
}

Line Count Optimization

// Generate with specific line count
const result = await engine.recalculateWithLineCount(imageData, 3000);

// Update configuration dynamically
engine.updateConfig({
  lineWeight: 0.3,
  lightnessPenalty: 0.9
});

API Reference

StringArtEngine

Constructor

constructor(config?: Partial<StringArtConfig>, options?: StringArtEngineOptions)

Methods

processImage(imageData: ImageData): Promise<StringArtResult>

Process an image to generate string art.

recalculateWithLineCount(imageData: ImageData, targetLines: number): Promise<StringArtResult>

Generate string art with a specific target line count.

analyzeImageQuality(imageData: ImageData): Promise<number>

Analyze image quality (returns 0-1 score).

getQualityAnalysis(imageData: ImageData): Promise<ImageQualityAnalysis>

Get detailed quality analysis with recommendations.

preprocessImage(imageData: ImageData): Promise<ImageData | null>

Preprocess image for better string art quality.

updateConfig(config: Partial<StringArtConfig>): void

Update engine configuration.

destroy(): void

Clean up resources.

Configuration Options

interface StringArtConfig {
  pinCount: number;              // Number of pins (50-500)
  imageSize: number;             // Image size in pixels
  minDistance: number;           // Min distance between pins
  maxLineCount: number;          // Maximum lines to generate
  lineWeight: number;            // Line opacity (0-1)
  lightnessPenalty: number;      // Dark area emphasis (0-1)
  lineNorm: number;              // Penalty calculation method
  useImportanceWeighting: boolean; // Use importance maps
  qualityMode: 'standard' | 'enhanced' | 'multipass' | 'parallel';
  useParallelProcessing: boolean; // Enable multithreading
  numThreads?: number;           // Number of threads
}

Result Structure

interface StringArtResult {
  lineSequence: number[];        // Pin sequence for drawing
  pinCoordinates: [number, number][]; // Pin positions
  processingTime: number;        // Time taken (ms)
  lineCount: number;             // Actual lines generated
  qualityScore?: number;         // Input quality score
}

Examples

React Component

import React, { useState, useRef } from 'react';
import { StringArtEngine } from 'string-art-engine';

function StringArtGenerator() {
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);
  const engineRef = useRef(new StringArtEngine());

  const handleImageUpload = async (event) => {
    const file = event.target.files[0];
    if (!file) return;

    setLoading(true);
    
    // Convert file to ImageData
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const img = new Image();
    
    img.onload = async () => {
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage(img, 0, 0);
      
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const stringArtResult = await engineRef.current.processImage(imageData);
      
      setResult(stringArtResult);
      setLoading(false);
    };
    
    img.src = URL.createObjectURL(file);
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleImageUpload} />
      {loading && <p>Generating string art...</p>}
      {result && (
        <div>
          <p>Generated {result.lineCount} lines in {result.processingTime.toFixed(0)}ms</p>
          {/* Render string art using result.lineSequence and result.pinCoordinates */}
        </div>
      )}
    </div>
  );
}

Canvas Rendering

function renderStringArt(canvas: HTMLCanvasElement, result: StringArtResult) {
  const ctx = canvas.getContext('2d');
  const { lineSequence, pinCoordinates } = result;
  
  // Clear canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // Set line style
  ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
  ctx.lineWidth = 0.5;
  
  // Draw lines
  for (let i = 0; i < lineSequence.length - 1; i++) {
    const fromPin = lineSequence[i];
    const toPin = lineSequence[i + 1];
    const from = pinCoordinates[fromPin];
    const to = pinCoordinates[toPin];
    
    ctx.beginPath();
    ctx.moveTo(from[0], from[1]);
    ctx.lineTo(to[0], to[1]);
    ctx.stroke();
  }
}

Building from Source

# Install dependencies
npm install

# Build WASM module
npm run build:wasm

# Build TypeScript
npm run build:ts

# Build everything
npm run build

Requirements

  • Node.js 16+
  • Modern browser with WebAssembly support
  • For building: Rust toolchain with wasm-pack

Performance Tips

  1. Use Web Workers: Enable useWorker: true for non-blocking processing
  2. Optimize Pin Count: Start with 200 pins, increase for more detail
  3. Quality vs Speed: Use 'standard' mode for speed, 'enhanced' for quality
  4. Parallel Processing: Enable for complex images with many pins
  5. Image Preprocessing: Use preprocessImage() for low-quality inputs

Browser Support

  • Chrome 57+
  • Firefox 52+
  • Safari 11+
  • Edge 16+

License

MIT

Contributing

Contributions welcome! Please read our contributing guidelines and submit pull requests to our repository.

Credits

Built with ❤️ by the StringRing team. Powered by Rust, WebAssembly, and modern web technologies.