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

framora

v0.1.0-alpha.1

Published

Framework-independent browser media engine for the Framora Photobooth ecosystem

Downloads

56

Readme

Framora (framora)

The Framework-Independent Browser Media Engine for Modern Photobooths & Interactive Kiosks.

TypeScript License: MIT Tests Bundle Size

framora is an enterprise-grade, 100% headless media processing SDK running natively in modern web browsers (Chrome, Edge, Safari, Firefox), Electron, and Tauri. It provides everything needed to build high-performance photobooth kiosks, mobile web apps, or web-based photo editors with React, Vue, Svelte, Angular, Solid, or Vanilla JavaScript.


Core Features

  • Headless & UI-Agnostic: Total freedom to design your UI with TailwindCSS, CSS Modules, or any UI kit.
  • Hardware Camera Controls: Resolution negotiation, digital/optical zoom, torch, camera flipping, and audio-visual countdowns.
  • Pro Image Editor: 50+ presets (Lightroom/Picsart style), tone adjustments (exposure, contrast, highlights, shadows, warmth, clarity, film grain, vignette, sharpen).
  • Dynamic Frame Composition: Connected-component BFS auto-detection for photo cutout slots, white knockout foreground overlays, decorative layers, and full CRUD template support.
  • Precision Print Queue: Photo lab sizing (2R, 3R, 4R, 5R, 6R, 8R, 10R, 12R, A4, A5, 2x6" Strip, CUSTOM), 300/600/1200 DPI, multi-up layouts, and pluggable silent print adapters for Tauri & Browser.
  • Embedded GIF89a Engine: Pure TypeScript animated GIF encoder with median-cut color quantization and LZW compression (0 external binary dependencies).
  • Video & Burst Recording: Multi-codec WebM/MP4 recording with automatic fallback.
  • Offline Resilience & Crash-Proof: IndexedDB persistent storage with power-loss recovery and session grouping.
  • Adaptive GPU Rendering: WebGPU / WebGL2 hardware acceleration with automated fallback to Canvas2D.

Installation

Install the package via NPM, Yarn, or PNPM:

# npm
npm install framora

# pnpm
pnpm add framora

# yarn
yarn add framora

Subpaths Map

Import only the modules you need for maximum bundle efficiency:

| Subpath | Key Classes & Functions | Purpose | | :--- | :--- | :--- | | framora | createMediaAsset, generateId, FramoraMediaError | Core media types, asset factory, error handling | | framora/camera | CameraManager, CapabilityManager | WebRTC camera enumeration, constraints, zoom, torch | | framora/capture | PhotoCapture, BurstCapture | Zero-allocation photo and burst snapshot capture | | framora/photoshoot| PhotoshootEngine | FSM for automated multi-pose photobooth sessions | | framora/image | ImageEditor, ImageEngine, FX_PRESETS | 50+ presets, parametric color grading & pixel filters | | framora/frame | FrameComposer, FrameDetector, detectPhotoAreas | Multi-slot photostrip composition & auto cutout detection | | framora/print | PrintManager, PrintQueue, PrintSize, BrowserPrintAdapter | Print sizing, 300 DPI scaling, queue, and crash recovery | | framora/gif | GifEngine, BurstGif | Pure TypeScript GIF89a encoder & burst GIF generator | | framora/video | VideoRecorder | WebM / MP4 video recording with pause/resume | | framora/storage | MediaStorage, SessionManager | IndexedDB persistent storage for assets & sessions | | framora/renderer | WebGL2Renderer, CanvasFallbackRenderer | GPU WebGL2 and 2D canvas accelerated rendering | | framora/adaptive | PerformanceManager, FPSMonitor, DeviceProfiler | Frame-rate monitoring and dynamic resolution scaling |


Quick Start: End-to-End Photobooth Workflow

Complete photobooth capture and printing flow:

import { CameraManager } from 'framora/camera';
import { PhotoCapture } from 'framora/capture';
import { ImageEditor } from 'framora/image';
import { FrameComposer, FrameDetector } from 'framora/frame';
import { PrintManager, BrowserPrintAdapter } from 'framora/print';
import { MediaStorage } from 'framora/storage';

// 1. Initialize Camera
const camera = new CameraManager();
const stream = await camera.initialize({ width: { ideal: 1920 }, height: { ideal: 1080 } });
camera.attach(document.querySelector<HTMLVideoElement>('#video-preview')!);

// 2. Capture a Photo
const capture = new PhotoCapture();
const rawPhoto = await capture.capture(document.querySelector<HTMLVideoElement>('#video-preview')!, {
  format: 'image/jpeg',
  quality: 0.95,
});

// 3. Edit Photo with a Filter Preset
const editor = new ImageEditor();
const editedPhoto = await editor.load(rawPhoto).applyPreset('film_portra400').export();

// 4. Auto-Detect Template Slots & Compose Frame
const templateFile = await (await fetch('/templates/vintage-strip-4slot.png')).blob();
const { frameDefinition } = await FrameDetector.detect(templateFile);
const composer = new FrameComposer();
const finalFramedAsset = await composer.compose({
  photos: [editedPhoto, editedPhoto, editedPhoto, editedPhoto],
  frame: frameDefinition,
  format: 'image/png',
});

// 5. Save & Print
const storage = new MediaStorage();
const printer = new PrintManager(storage, new BrowserPrintAdapter());
await printer.print(finalFramedAsset, '4R', { mode: 'FILL', copies: 2 });

Module Guides & API Examples

1. Camera Engine (framora/camera)

Manage camera hardware, resolution negotiation, and live WebRTC streams.

import { CameraManager } from 'framora/camera';

const camera = new CameraManager();

// Start camera stream
const stream = await camera.initialize({
  deviceId: 'optional-device-id',
  facingMode: 'user', // 'user' | 'environment'
  width: { ideal: 1920 },
  height: { ideal: 1080 },
  frameRate: { ideal: 30, max: 60 },
});

// Attach to video element
const videoEl = document.getElementById('camera-feed') as HTMLVideoElement;
camera.attach(videoEl);

// Switch camera (front / back / external USB)
await camera.switchCamera('environment');

// Hardware zoom & flashlight control (supported mobile devices)
await camera.setZoom(2.0);
await camera.setTorch(true);

// List available devices
const devices = await camera.getAvailableDevices();

// Release camera hardware
camera.stop();

2. High-Speed Photo Capture (framora/capture)

Zero-allocation snapshot capture using native createImageBitmap.

import { PhotoCapture, BurstCapture } from 'framora/capture';

const photoCapture = new PhotoCapture();

// Single snapshot
const photo = await photoCapture.capture(videoElement, {
  format: 'image/jpeg', // 'image/jpeg' | 'image/png' | 'image/webp'
  quality: 0.95,
  mirror: true,         // Mirror selfie capture
  targetWidth: 1920,
  targetHeight: 1080,
});

// Burst capture sequence (e.g. 5 shots, 200ms apart)
const burstCapture = new BurstCapture();
const burstPhotos = await burstCapture.captureBurst(videoElement, {
  count: 5,
  intervalMs: 200,
});

3. Automated Photoshoot Engine (framora/photoshoot)

An automated Finite State Machine (FSM) for photobooth countdowns, pose sequences, and retakes.

import { PhotoshootEngine } from 'framora/photoshoot';

const photoshoot = new PhotoshootEngine({
  totalPoses: 4,
  countdownSeconds: 3,
  reviewSeconds: 2,
});

// Listen to session events
photoshoot.on('stateChange', (state) => console.log('State:', state));
photoshoot.on('countdown', (secondsLeft) => console.log('Countdown:', secondsLeft));
photoshoot.on('capturing', (poseIndex) => console.log(`Taking photo ${poseIndex + 1}...`));
photoshoot.on('shot', ({ index, asset }) => console.log(`Pose ${index + 1} captured!`, asset));
photoshoot.on('complete', (photos) => console.log('All photos captured:', photos));

// Start the sequence
const sessionPhotos = await photoshoot.start(async (poseIndex) => {
  return await photoCapture.capture(videoElement);
});

// Retake a single pose if the customer didn't like pose #2
const retakenPhoto = await photoshoot.retake(1, async () => {
  return await photoCapture.capture(videoElement);
});

4. Image Editor & Presets (framora/image)

Chainable image editing API with Lightroom-grade color adjustments.

import { ImageEditor } from 'framora/image';

const editor = new ImageEditor();

// Chain adjustments and export
const editedAsset = await editor
  .load(photoAsset)
  .adjust({
    brightness: 10,     // -100 to +100
    contrast: 15,       // -100 to +100
    highlights: -20,    // -100 to +100 (recover highlights)
    shadows: 25,        // -100 to +100 (lift dark shadows)
    temperature: 12,    // -100 (Cool) to +100 (Warm)
    saturation: 10,     // -100 to +100 (-100 = B&W)
    clarity: 15,        // -50 to +100 (micro-contrast)
    sharpen: 20,        // 0 to 100
    grain: 10,          // 0 to 100 (film grain noise)
    vignette: 15,       // 0 to 100
  })
  .rotate(90)           // 0 | 90 | 180 | 270
  .flip('horizontal')   // 'horizontal' | 'vertical' | 'both'
  .export({ format: 'image/jpeg', quality: 0.95 });

// Apply Built-in Presets
await editor.load(photoAsset).applyPreset('film_portra400').export();
await editor.load(photoAsset).applyPreset('vintage_warm').export();
await editor.load(photoAsset).applyPreset('mono_noir').export();

// Register a Custom Brand Preset
editor.registerPreset({
  id: 'brand_sunset',
  name: 'Brand Sunset Glow',
  category: 'artistic',
  adjust: { temperature: 35, saturation: 15, clarity: 10, vignette: 20 },
});

5. Dynamic Frame Composition & Cutout Detection (framora/frame)

Automatically detect cutout slots in PNG/JPEG frame templates or construct them dynamically.

import { FrameDetector, FrameComposer, type FrameDefinition } from 'framora/frame';

// 1. Auto-detect photo slots from an uploaded frame template
const { frameDefinition, slotCount } = await FrameDetector.detect(templateBlob, {
  mode: 'auto',          // 'auto' | 'alpha' | 'white' | 'borders'
  alphaThreshold: 35,    // Transparency threshold
  whiteThreshold: 235,   // White window knockout threshold
});

console.log(`Detected ${slotCount} photo cutout slots!`);

// 2. Adjust or define custom layout properties (100% JSON-serializable for CRUD)
frameDefinition.photoAreas[0].fit = 'cover'; // 'cover' | 'contain' | 'fill'
frameDefinition.photoAreas[0].rotation = 2.5; // Custom angle

// 3. Compose multiple photos onto the frame
const composer = new FrameComposer();
const framedAsset = await composer.compose({
  photos: [photo1, photo2, photo3, photo4],
  frame: frameDefinition,
  format: 'image/png',
  quality: 0.95,
  onProgress: ({ stage, progress }) => console.log(`Composing: ${stage} ${(progress * 100).toFixed(0)}%`),
});

6. Precision Print Queue & Lab Sizing (framora/print)

Manage print queues, photo lab dimensions (4R, 2x6" Strip, A4), multi-up layouts, and print adapters.

import { PrintManager, BrowserPrintAdapter, PrintSize, type PrintJob } from 'framora/print';
import { MediaStorage } from 'framora/storage';

const storage = new MediaStorage();
const printer = new PrintManager(storage, new BrowserPrintAdapter());

// Enqueue and print a 4R photo (4x6 inches / 102x152 mm) at 300 DPI
const job: PrintJob = await printer.print(
  framedAsset,
  '4R',
  {
    mode: 'FILL',           // 'FILL' | 'FIT' | 'CROP' | 'STRETCH'
    orientation: 'PORTRAIT',// 'PORTRAIT' | 'LANDSCAPE' | 'AUTO'
    borderless: true,
    copies: 2,
  },
  {
    dpi: 300,
    maxAttempts: 3,
    metadata: { orderId: 'ORD-9876' },
  }
);

// Custom Size in mm or inch
const customJob = await printer.print(framedAsset, 'CUSTOM', { mode: 'FILL' }, {
  customDimensions: { width: 50, height: 150, unit: 'mm' }, // 2x6 photostrip
  dpi: 300,
});

// Check queue & recover stuck jobs on app launch (power-loss safety)
await printer.recoverOnStartup();
const pendingJobs = await printer.queue.list();

Desktop / Tauri Silent Print Integration

In Tauri desktop apps, create a custom TauriPrintAdapter for zero-dialog native printing:

import { invoke } from '@tauri-apps/api/core';
import type { PrintAdapter, PrintJob } from 'framora/print';
import type { MediaAsset } from 'framora';

export class TauriPrintAdapter implements PrintAdapter {
  async print(job: PrintJob, asset: MediaAsset): Promise<void> {
    const bytes = Array.from(new Uint8Array(await asset.blob.arrayBuffer()));
    await invoke('native_silent_print', {
      jobId: job.id,
      imageBytes: bytes,
      copies: job.layout.copies,
      paperSize: job.size,
    });
  }
}

// Use with PrintManager:
const tauriPrinter = new PrintManager(storage, new TauriPrintAdapter());

7. Pure TypeScript Animated GIF Engine (framora/gif)

Generate animated GIFs with palette quantization and LZW compression directly in the browser (no WebAssembly or C dependencies required).

import { BurstGif, GifEngine } from 'framora/gif';

// 1. Create a looping GIF from a burst photo sequence
const burstGif = new BurstGif();
const gifAsset = await burstGif.createFromAssets(burstPhotos, {
  fps: 10,              // Frame rate (default: 12)
  width: 600,           // Scaled output width
  quality: 128,         // Color palette size (2 to 256 colors)
  loop: 0,              // 0 = loop forever
});

// 2. Or encode raw ImageData frames
const gifEngine = new GifEngine();
const gifBlob = await gifEngine.encode(imageDataFrames, {
  delay: 100, // ms per frame
  quality: 256,
});

8. Video Recording (framora/video)

Record live camera streams into WebM / MP4 video files with automatic codec negotiation.

import { VideoRecorder } from 'framora/video';

const recorder = new VideoRecorder();

// Start recording from active camera stream
recorder.start(stream, {
  mimeType: 'video/webm;codecs=vp9', // Automatic multi-codec fallback
  videoBitsPerSecond: 2_500_000,
});

// Pause / Resume during session
recorder.pause();
recorder.resume();

// Stop recording and retrieve video MediaAsset
const videoAsset = await recorder.stop();
console.log('Video recorded:', videoAsset.blob.size, 'bytes');

9. Persistent Media Storage & Sessions (framora/storage)

Persistent binary storage backed by IndexedDB for offline kiosks.

import { MediaStorage, SessionManager } from 'framora/storage';

const storage = new MediaStorage();
await storage.open();

// Save, get, and delete assets
await storage.put(photoAsset);
const retrieved = await storage.get(photoAsset.id);
const allFrames = await storage.queryByType('frame');
await storage.delete(photoAsset.id);

// Session Manager: Group assets under customer sessions
const sessionMgr = new SessionManager(storage);
const session = await sessionMgr.create({
  customerId: 'CUST-101',
  theme: 'vintage',
});

await sessionMgr.addAsset(session.id, photoAsset);
const activeSession = await sessionMgr.get(session.id);

10. Real-Time Logging & Diagnostics (FramoraLogger)

Subscribe to internal system events to display real-time logs, errors, and performance traces in your web application UI or kiosk diagnostics screen.

import { FramoraLogger, type LogEntry } from 'framora';

// 1. Listen to all internal logs across the entire SDK in real-time
const unsubscribe = FramoraLogger.onLog((entry: LogEntry) => {
  console.log(`[${entry.namespace}] (${entry.level.toUpperCase()}):`, entry.message);
  
  // Example: Append directly to your web UI state / terminal log component
  // setLogsState((prev) => [...prev, entry]);
});

// 2. Set global minimum log level ('debug' | 'info' | 'warn' | 'error')
FramoraLogger.setGlobalLevel('debug');

// 3. Retrieve stored history (up to last 200 log entries)
const recentLogs = FramoraLogger.getHistory(50);

// 4. Clean up listener when UI component unmounts
// unsubscribe();

Testing & Development

Run unit tests, builds, and development servers locally:

# Run 88 unit tests across all modules
npm test

# Run tests with code coverage report
npm run test:coverage

# TypeScript typechecking
npm run typecheck

# Linter (Oxlint)
npm run lint

# Build production bundle (dist/framora.js, dist/framora.cjs, dist/*.d.ts)
npm run build

# Run interactive documentation site & playground
npm run docs

License

MIT (c) Framora — Free for personal and commercial photobooth applications.