{"slug": "show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml", "title": "Show HN: BorderCut – Remove image backgrounds locally in 8 kB, without ML", "summary": "BorderCut, a new open-source tool from developer Kong75, removes image backgrounds locally in about 8 kB gzipped without machine learning, running entirely on-device in browsers, Node.js, and Dart/Flutter. The TypeScript core is 7.9 kB minified+gzipped and the browser adapter is 8.7 kB, both with zero runtime dependencies, enabling offline processing without servers, API keys, or GPUs. The project is pre-1.0 alpha software, MIT licensed, and not yet published to package registries.", "body_md": "**Background removal in about 8 kB gzipped. Runs entirely on your device.**\n\nBorderCut removes separable backgrounds with a tiny, dependency-free algorithm. Embed it in a browser, Web Worker, Node.js application, or a Dart/Flutter client. Images stay on the device doing the processing. Once the code is loaded, it works offline: no server, API key, model download, or GPU is required.\n\nThe TypeScript core is **7.9 kB minified + gzipped**. The optional browser file-to-PNG adapter, including the entire core, is **8.7 kB minified + gzipped**. Both have **zero runtime dependencies**. The native Dart core is also dependency-free; the Flutter adapter adds engine codecs and isolate processing.\n\nUse it for product shots, graphics, and other images whose subjects are distinguishable from the background at the image boundary. Difficult scenes can need Remove/Keep guidance; this is a classical image-processing algorithm with [known limits](#known-limits).\n\n``` js\nimport { removeBackground } from '@bordercut/core';\n\nconst { image, alpha } = removeBackground({ width, height, data: rgba });\n```\n\nIn a browser, pass a local `File` or `Blob` directly to the optional adapter:\n\n``` js\nimport { removeBackgroundFromBlob } from '@bordercut/core/browser';\n\nconst { blob } = await removeBackgroundFromBlob(file); // Transparent PNG, processed locally.\n```\n\nFor a site without a bundler, build the package with `npm ci && npm run build --workspace @bordercut/core`, copy `packages/typescript/dist/browser.min.js` into your site, and import it from a module script:\n\n``` js\nimport { removeBackgroundFromBlob } from './browser.min.js';\n```\n\nThat file is a standalone ES module with no additional imports. Use `core.min.js` instead when you already have decoded RGBA pixels. Both files also ship in the npm tarball. Serve them with your own application; no CDN or hosted processing service is required. For responsive interfaces, run processing in a [Web Worker](/kong75/bordercut/blob/main/examples/minimal-web/src/removal-worker.ts).\n\nPackages are not published yet; clone this repository and use the [working examples](#supported-runtimes) to get started. The project is **pre-1.0 alpha software**, MIT licensed, with a versioned algorithm contract. Package APIs may change between minor releases.\n\n| Standalone module | Minified | Minified + gzip | Minified + Brotli | \n|---|---|---|---|\n| `core.min.js` | 20.6 kB | 7.9 kB | 6.9 kB | \n| `browser.min.js` | 22.7 kB | 8.7 kB | 7.7 kB | \n\nThese are measured sizes of the complete standalone JavaScript modules, using esbuild minification, gzip level 9, and Brotli quality 11; 1 kB = 1,000 bytes. The browser row includes the core, so the two rows are alternatives. Source maps, documentation, and example UI are excluded. Compressed transfer sizes are not the npm archive size, installation size, runtime memory use, or a Dart/Flutter app-size claim. Actual transfer size depends on server compression.\n\nRun `npm run size` to reproduce the measurement, or `npm run docs:size` to refresh this table and the [exact byte counts and bundle hashes](/kong75/bordercut/blob/main/docs/size/latest.json). CI enforces gzip limits of 8,000 bytes for the core and 9,000 bytes for the browser adapter, and rejects runtime dependencies or external imports in the standalone modules.\n\n| Example | Before | After | \n|---|---|---|\n| Butterfly specimen · fine antennae and patterned wings |  |  | \n| Vintage botanical print · pale petals and branching stems |  |  | \n| Dark flower photo · low-contrast petal edges |  |  | \n| Product photo · white background |  |  | \n\nActual BorderCut outputs, without manual retouching or correction strokes. The butterfly disables `recoverPaleSubject` to reduce the retained pale rim; other settings are defaults. The flower uses an explicit crop, identical before and after processing. The checkerboard displays transparency. Processing uses images up to 1024 pixels on the longest edge.\n\nThese examples also show the limits: thin edge halos remain, some very dark petal detail merges with the backdrop, and the apple retains part of its contact shadow. Selected examples are not a general segmentation accuracy benchmark. [Exact inputs, settings, source credits, and reproduction](/kong75/bordercut/blob/main/docs/showcase/README.md).\n\nButterfly photograph by [Notafly](https://commons.wikimedia.org/wiki/File:Papiliorex_Oberth%C3%BCr,_1886.JPG), [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/); its resized and background-removed derivatives retain that license. Botanical illustration by Pierre-Joseph Redouté (public domain); flower photograph credited to Ameen Fahmy on Commons (CC0); apple photograph by Amada44 (public domain).\n\nMeasured 2026-09-06 with Node v24.11.1 on Intel(R) Core(TM) Ultra 9 275HX, win32 x64. Algorithm v1.\n\n| Input | Pixels | Median | p95 | \n|---|---|---|---|\n| butterfly | 512 × 384 | 29.9 ms | 59.9 ms | \n| butterfly | 1024 × 768 | 102.6 ms | 113.7 ms | \n| butterfly | 2048 × 1536 | 414.8 ms | 440.9 ms | \n| botanical | 379 × 512 | 28.5 ms | 40.4 ms | \n| botanical | 758 × 1024 | 109.8 ms | 129.3 ms | \n| botanical | 944 × 1275 | 172.5 ms | 198.2 ms | \n| flower | 512 × 435 | 38.6 ms | 41.9 ms | \n| flower | 1024 × 871 | 167.5 ms | 190.4 ms | \n| flower | 2048 × 1741 | 717.3 ms | 769.7 ms | \n| apple | 512 × 422 | 30.1 ms | 41.5 ms | \n| apple | 1024 × 844 | 126.4 ms | 140.7 ms | \n| apple | 2011 × 1657 | 535.3 ms | 572.3 ms | \n\n5 warm-up calls and 20 timed calls per row. Times cover the synchronous TypeScript algorithm and its allocations; they exclude decoding, cropping, resizing, PNG encoding, file I/O, and worker overhead. p95 uses nearest rank. Inputs, crops, and options match the showcase cases; images are never enlarged.\n\nThese are measurements on one development machine, not latency guarantees or Dart/Flutter/browser measurements. Content, settings, runtime, hardware, and background activity affect timings. [Raw samples, source hashes, and environment](/kong75/bordercut/blob/main/docs/benchmarks/latest.json) are retained so results can be compared honestly.\n\nReproduce with `npm ci && npm run benchmark`. Regenerate previews separately with `npm run docs:showcase`.\n\n| Target | Integration | \n|---|---|\n| Modern browsers | ES2022 pixel core or native Blob/PNG adapter; [minimal web example](/kong75/bordercut/blob/main/examples/minimal-web) | \n| Web Workers | The same TypeScript core off the UI thread; [worker example](/kong75/bordercut/blob/main/examples/minimal-web/src/removal-worker.ts) | \n| Node.js 20.19+ | Pixel core with your chosen codecs; [file-to-PNG example using sharp](/kong75/bordercut/blob/main/examples/node-sharp) | \n| Dart | Native pixel implementation; [package and usage](/kong75/bordercut/blob/main/packages/dart) | \n| Native Flutter apps | Engine codecs and isolate processing; [adapter and usage](/kong75/bordercut/blob/main/packages/flutter) | \n\nTypeScript and Dart implement algorithm v1 and share the same options, fixtures, and byte-exact fixture alpha output. Image decoding, file access, and platform integration stay in adapters. The Flutter isolate adapter targets native apps; browser apps use the TypeScript implementation.\n\n```\nbordercut/\n  spec/                    Language-neutral algorithm and schemas\n  fixtures/v1/             Shared cross-language conformance cases\n  packages/\n    typescript/            Publishable @bordercut/core package\n    dart/                  Pure-Dart algorithm-v1 package\n    flutter/               Flutter codecs and isolate adapter\n  examples/\n    minimal-web/           Unbranded Web Worker integration example\n    node-sharp/            Runnable Node file adapter example\n```\n\nThe core packages accept decoded RGBA bytes. Image codecs, resizing, UI, files, and networking stay in adapters and applications.\n\nFor TypeScript, Node.js 20.19 or newer and npm are required.\n\n```\nnpm ci\nnpm run check\nnpm run dev\n```\n\nThe example is served locally by Vite. Its production build is written to `examples/minimal-web/dist/`; the TypeScript library is written to `packages/typescript/dist/`.\n\nFor Dart and Flutter workspace development, use Flutter 3.41.7, matching CI. Run these commands from the repository root:\n\n```\nflutter pub get\ndart analyze packages/dart\ndart test packages/dart\nflutter analyze packages/flutter\nflutter test packages/flutter\njs\nimport { removeBackground } from '@bordercut/core';\n\nconst result = removeBackground(\n  { width, height, data: rgba },\n  {\n    tolerance: 46,\n    edgeProtection: 58,\n    feather: 2,\n    cleanup: 1,\n    protectCenter: true,\n    recoverPaleSubject: true,\n    interiorBackground: false,\n  },\n  {\n    strokes: [\n      {\n        kind: 'background', // remove this painted line\n        radius: 8,\n        points: [{ x: 24, y: 30 }, { x: 80, y: 42 }],\n      },\n    ],\n  },\n);\n\n// result.image: transparent RGBA image\n// result.alpha: one-byte alpha mask\n// result.diagnostics.algorithmVersion: portable algorithm contract version\n```\n\nThe package has no runtime dependencies. The default core entry does not decode or encode image files.\n\nFor a one-call browser workflow, the optional `@bordercut/core/browser` subpath accepts a `Blob` or `File` and returns the complete result plus a transparent PNG `Blob`:\n\n``` js\nimport { removeBackgroundFromBlob } from '@bordercut/core/browser';\n\nconst { blob, diagnostics } = await removeBackgroundFromBlob(file);\n```\n\nThis adapter uses browser-native codecs and remains separate from the default entry point. Interactive applications should process pixels in a Web Worker, as demonstrated by `examples/minimal-web`. Node applications can follow the runnable `examples/node-sharp` integration.\n\nInputs are validated at runtime. Width and height must be positive safe integers, RGBA data must be a sufficiently large `Uint8ClampedArray`, numeric options must be finite and within their documented ranges, and correction samples and strokes must fall inside the image.\n\nThe optional third argument accepts `{ samples, strokes }`. Samples influence classification and connected-region decisions within an engine-derived area. Brush strokes seed smart, color- and edge-aware local region guidance: `background` guides removal and `foreground` guides subject retention. Their effect may extend beyond the painted line, but bounded reach prevents a stroke from following matching colors across the image. Strokes run in array order after the global baseline is stable: a new Remove stroke can only lower alpha, while a new Keep stroke can only raise it. Stroke radius and points use image-pixel coordinates, and later strokes override earlier grown corrections where they reach. Passing a sample array directly remains supported for compatibility.\n\nThe pure Dart package exposes the same pixel-level contract without JavaScript, FFI, network access, or runtime dependencies:\n\n```\nfinal result = removeBackground(\n  PixelImage(width: width, height: height, data: rgba),\n  guidance: const RemovalGuidance(\n    strokes: [\n      BrushStroke(\n        kind: SampleKind.background,\n        radius: 8,\n        points: [StrokePoint(x: 24, y: 30), StrokePoint(x: 80, y: 42)],\n      ),\n    ],\n  ),\n);\n```\n\nThe separate Flutter adapter keeps engine concerns out of the core package. It can decode supported image bytes, run the algorithm outside the UI isolate, and encode the result as a transparent PNG:\n\n```\nfinal output = await removeBackgroundFromBytes(encodedImageBytes);\nfinal transparentPng = output.png;\n```\n\nSee [`packages/dart`](/kong75/bordercut/blob/main/packages/dart) and [` packages/flutter`](/kong75/bordercut/blob/main/packages/flutter)\nfor complete package usage.\n\n1. Sample and cluster plausible background colors around the image perimeter.\n2. Fit a spatial color plane when the boundary supports a smooth gradient.\n3. Score pixels using background distance, edge strength, center protection, and optional correction markers.\n4. Flood-fill only background connected to trusted exterior seeds.\n5. Recover pale subject material using blurred color evidence when the learned background is sufficiently regular.\n6. Seal narrow, low-contrast false cutouts while preserving wider or high-contrast openings.\n7. Clean and feather the alpha mask, then decontaminate semi-transparent edge colors.\n\nSee [the algorithm v1 specification](/kong75/bordercut/blob/main/spec/algorithm-v1.md) for the portable contract.\n\n- **Background reach** broadens or narrows accepted background colors.\n- **Edge protection** makes visible transitions harder to cross.\n- **Edge softness** feathers the alpha boundary.\n- **Speck cleanup** removes isolated pixel decisions.\n- **Protect the center** adds a weak centered-subject prior.\n- **Recover pale subject** repairs light material that resembles a regular backdrop.\n- **Open interior holes** removes confident enclosed background regions.\n- **Remove brush** guides removal through nearby, visually similar background.\n- **Keep brush** guides subject retention through nearby, visually similar material.\n- **Brush size** controls how much evidence each correction stroke supplies.\n\nThe included examples demonstrate these controls and library integration.\n\nEvery implementation should:\n\n- expose the option names and defaults in `spec/options.schema.json` ;\n- report its supported algorithm version;\n- pass the shared cases in `fixtures/v1/cases.json` ;\n- remain deterministic for identical bytes, dimensions, options, and correction guidance.\n\nTypeScript remains the specification reference. The Dart port is independently implemented and must retain byte-exact alpha compatibility with every published algorithm-v1 fixture.\n\nNo rule-based tool can infer semantics in every image. A white subject in snow, a subject covering every edge, motion blur, or highly detailed scenery may need correction strokes or a semantic model. BorderCut is strongest on product photos, portraits with separable backgrounds, chroma-key images, studio gradients, icons, screenshots, and controlled illustrations.\n\nSee [CONTRIBUTING.md](/kong75/bordercut/blob/main/CONTRIBUTING.md). Behavioral changes should update tests and, when applicable, the shared portable fixtures. Community expectations, support, security reporting, governance, and release history are documented in:\n\nThe local release checklist is in [RELEASING.md](/kong75/bordercut/blob/main/RELEASING.md). Public hosting and package publication remain manual maintainer decisions.\n\nCode: MIT. Showcase images retain their [documented source licenses](/kong75/bordercut/blob/main/docs/showcase/README.md).", "url": "https://wpnews.pro/news/show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml", "canonical_source": "https://github.com/kong75/bordercut", "published_at": "2026-09-07 13:00:04+00:00", "updated_at": "2026-09-07 13:28:20.586318+00:00", "lang": "en", "topics": ["ai-tools"], "entities": ["BorderCut", "Kong75", "TypeScript", "Dart", "Flutter", "Node.js", "MIT"], "alternates": {"html": "https://wpnews.pro/news/show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml", "markdown": "https://wpnews.pro/news/show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml.md", "text": "https://wpnews.pro/news/show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml.txt", "jsonld": "https://wpnews.pro/news/show-hn-bordercut-remove-image-backgrounds-locally-in-8-kb-without-ml.jsonld"}}