A web app to help you design things, local, offline, on device. In your browser.

TypeScript 53.1% JSON 45.6% CSS 0.9% Markdown 0.3% JavaScript 0.1%
background-removal.ts 142 lines (3 KB)
// AI Background Removal using @imgly/background-removal
// Runs entirely in-browser - no server needed
// License: AGPL (free for open source projects)

import { removeBackground, Config } from '@imgly/background-removal';

export interface BackgroundRemovalResult {
  success: boolean;
  blob?: Blob;
  dataUrl?: string;
  error?: string;
}

export interface BackgroundRemovalProgress {
  progress: number;
  stage: 'loading' | 'processing' | 'complete' | 'error';
  message: string;
}

type ProgressCallback = (progress: BackgroundRemovalProgress) => void;

// Default configuration for background removal
const defaultConfig: Partial<Config> = {
  // Use isnet_quint8 model for faster processing
  model: 'isnet_quint8',
  // Output format
  output: {
    format: 'image/png',
    quality: 1,
  },
};

/**
 * Remove background from an image
 * @param imageSource - Image source (URL, Blob, or File)
 * @param onProgress - Progress callback
 * @param config - Optional configuration overrides
 */
export async function removeImageBackground(
  imageSource: string | Blob | File,
  onProgress?: ProgressCallback,
  config?: Partial<Config>
): Promise<BackgroundRemovalResult> {
  try {
    onProgress?.({
      progress: 0,
      stage: 'loading',
      message: 'Loading AI model...',
    });

    const mergedConfig = {
      ...defaultConfig,
      ...config,
      progress: (key: string, current: number, total: number) => {
        const progress = Math.round((current / total) * 100);
        onProgress?.({
          progress,
          stage: 'processing',
          message: `Processing: ${progress}%`,
        });
      },
    } as Config;

    onProgress?.({
      progress: 10,
      stage: 'processing',
      message: 'Analyzing image...',
    });

    const blob = await removeBackground(imageSource, mergedConfig);

    onProgress?.({
      progress: 100,
      stage: 'complete',
      message: 'Background removed!',
    });

    // Convert blob to data URL for immediate use
    const dataUrl = await blobToDataUrl(blob);

    return {
      success: true,
      blob,
      dataUrl,
    };
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';

    onProgress?.({
      progress: 0,
      stage: 'error',
      message: errorMessage,
    });

    return {
      success: false,
      error: errorMessage,
    };
  }
}

/**
 * Convert a Blob to a data URL
 */
function blobToDataUrl(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result as string);
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}

/**
 * Convert a data URL to a Blob
 */
export function dataUrlToBlob(dataUrl: string): Blob {
  const arr = dataUrl.split(',');
  const mime = arr[0].match(/:(.*?);/)?.[1] || 'image/png';
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}

/**
 * Check if background removal is supported in this environment
 */
export function isBackgroundRemovalSupported(): boolean {
  // Requires WebGL support
  try {
    const canvas = document.createElement('canvas');
    const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    return gl !== null;
  } catch {
    return false;
  }
}

About

A web app to help you design things, local, offline, on device. In your browser.

0 stars
0 forks