HushShotWiki
Documentation

HushShot Wiki

Everything about HushShot, the free screenshot and PDF redaction tool that runs entirely in your browser: how to use it, what it detects, why the redaction can't be undone, and how to build, extend and deploy it.

100% on-device MIT licensed No accounts · no tracking Works offline

01Overview

A privacy shield for the screenshots and documents you share every day.

People paste screenshots into chat apps, AI assistants, bug trackers and social media constantly. Those images often contain a card number, a home address, a face, an API key or a QR code that logs someone in, and the file itself can carry hidden GPS coordinates and camera serial numbers. Drawing black boxes by hand is slow, easy to get wrong, and leaves the metadata untouched.

HushShot finds that information for you, hides it, and writes a clean copy of the file with no hidden metadata. It has no backend: the text recognition, face detection and barcode scanning all run in your browser tab.

🔍 Finds what matters

Cards, SSNs, emails, phones, addresses, account numbers, birth dates, API keys, faces, barcodes and QR codes.

🧼 Strips hidden data

GPS, camera and serial numbers, capture dates, author info. The saved file is re-checked.

🔒 Truly private

No server. A strict Content-Security-Policy blocks the page from sending your file anywhere.

💥 Irreversible

Pixels are averaged and jittered before smoothing; PDFs are flattened. Nothing to "un-blur".

Who it's for

  • Job seekers sharing pay stubs, offer letters or ID scans.
  • Remote workers and support staff sharing screens, logs and error screenshots.
  • Developers posting terminal output that might contain tokens or keys.
  • Creators posting photos where bystanders' faces or location data should not be public.
  • Students and professionals uploading forms to AI tools.

02Quick start

Use the hosted app, run it locally, or wrap it as a desktop app.

Use it online

Open syasj.github.io/hushshot. There's nothing to install and no sign-up. Drop a file, or click one of the eight built-in example documents to see it work. Once the page has loaded, it keeps working with no internet connection.

Run it locally

Requires Node.js 18+ and a modern browser (Chrome/Edge 119+, Firefox 121+, Safari 17.4+).

$ git clone https://github.com/SYasJ/hushshot
$ cd hushshot
$ npm install        # also copies OCR / face / PDF assets into public/vendor
$ npm run dev        # → http://localhost:5173

Production build: plain static files you can host anywhere (GitHub Pages, Netlify, S3, a USB stick):

$ npm run build      # → dist/
$ npm run preview    # serve dist/ at http://localhost:4173
ℹ️

The strict Content-Security-Policy is injected into production builds only, because Vite's dev server needs inline scripts for hot reload. Use npm run preview when you want to test the locked-down version.

Desktop app (experimental)

A small Electron shell serves the built app from a loopback-only server and blocks every request that isn't to that server, so it's offline by design.

$ npm run desktop        # build + launch
$ npm run desktop:pack   # installers (dmg / nsis / AppImage) → release/

03User guide

From dropping a file to sharing a clean copy, usually in under a minute.

  1. DropDrag an image or PDF onto the page, click to browse, or paste a screenshot with Ctrl/⌘+V.
  2. ScanText recognition, barcode and face detection run in sequence. Detected items are hidden as soon as each step finishes.
  3. ReviewClick a box to un-hide or re-hide it, switch whole categories off, or drag to hide anything that was missed.
  4. SaveDownload the sanitized file, or copy it to the clipboard. The output is re-opened and checked for metadata.
HushShot workspace: the redacted pay stub on the left, the scan results, category switches, style controls and Save button on the right
The workspace: preview with detection boxes (left), scan progress, categories, style and export (right).

Opening files

InputSupportedNotes
ImagesPNG, JPG, WebP, GIF, BMP, AVIFEXIF orientation is applied, so rotated phone photos come out upright. Transparency is flattened onto white. Images larger than 9,000 px on a side are scaled down.
PDFUp to 60 pagesEach page is rendered at 144 dpi. Extra pages beyond 60 are skipped, with a notice.
ClipboardAny imagePaste anywhere on the page. Handy right after taking a screenshot.
HEIC / HEIFNoBrowsers can't decode HEIC. On iPhone: Settings → Camera → Formats → Most Compatible, or convert to JPG first.

HushShot handles one document at a time. If several files are dropped, the first one is opened.

Reviewing detections

  • Solid cyan box: the item is hidden. Hover to see its category and a masked hint (e.g. j••••••••).
  • Dashed amber box: the item is not hidden. Click it to hide it again.
  • Categories panel: each category has a count and a switch. Turning a category off reveals all of its items at once, which is useful if, say, you want phone numbers visible but everything else hidden.
  • Draw your own box: press and drag on any empty part of the image. Manual boxes appear under Drawn by you and have a red × to remove them.
  • Hold to compare: press and hold 👁 Hold to compare to see the original. Release to return to the redacted view.
  • Multi-page PDFs: use the ‹ › buttons or the arrow keys. Every page is scanned; counts include all pages.
⚠️

Always glance at the preview before sharing. Detection is good, not perfect: names, handwriting and unusual fonts can be missed. The drag-to-hide tool covers anything the scanner didn't catch.

Styles & strength

StyleLooks likeBest for
Blur defaultSoft, smooth smudgeSocial posts, chat apps, screenshots for AI tools
PixelateClassic mosaicWhen it should be obvious that something was hidden
BlackoutSolid bar (ellipse over faces)Legal, HR, medical, anything where you want zero doubt

The Strength slider makes the mosaic coarser. All three styles destroy the underlying detail. See Irreversible redaction.

Saving & copying

SourceOutput formatsDetails
ImagePNG (lossless) or JPGJPG is re-encoded at quality 93. The default matches the input type.
PDFPDF (pages flattened)Each page becomes an image. The text layer is gone, so the output isn't text-selectable.
Any imageCopy to clipboard (PNG)Paste straight into Slack, Discord, email or an AI chat. Not available for PDFs.

Saved files are named <original>-redacted.<ext>. After saving, a note confirms how many regions were hidden, how many metadata fields were removed, and that the re-check found 0 metadata fields in the output.

ℹ️

The file name isn't scrubbed beyond adding -redacted. If it contains your name, rename the file before sharing it.

Keyboard shortcuts

KeysAction
Ctrl/⌘ + VOpen a screenshot from the clipboard
Enter / Space on the drop zoneOpen the file picker
← →Previous / next PDF page
Enter / Space on a categoryToggle that category
Hold Space on Hold to compareShow the original while held
TabMove between the panel controls (buttons, categories, style, slider, format)

04Examples

Real output from the production build. All people, numbers and addresses are fictional; portraits are AI-generated.

Every example can be opened with one click from the app's landing page. Each sheet shows the original followed by Blur, Pixelate and Blackout.

05What gets detected

Text rules run on OCR output; faces and barcodes use dedicated detectors.

Text patterns

Each OCR line is matched against the rules in src/detect/patterns.js. Matches are mapped back to pixel boxes, trimmed to just the sensitive part (e.g. only the address in Email:jane@x.com), and extended across line breaks for phones, addresses and cards.

CategoryExamplesHow it's recognised
💳 Credit cards4242 4242 4242 4242, Amex 3782 822463 10005, masked **** 424213–19 digits + Luhn checksum. Well-formed 4-4-4-4 / 4-6-5 groups are kept even if OCR misreads one digit.
🪪 SSN / national ID123-45-6789, SSN: 123456789, TIN, EIN, SINPattern plus invalid-range filter (000, 666, 9xx), or a label followed by digits.
✉️ Emailsjane.doe+work@example.co.ukRFC-style address pattern.
📞 Phone numbers(415) 555-2671, +1 415 555 2671, +44 20 7946 0958North American formats and international + numbers with 8–15 digits.
🏠 Addresses742 Evergreen Terrace, Apt 4B, Springfield, IL 62704, PO Box 12Number + street suffix, City-State-ZIP, Canadian and UK postcodes, PO boxes.
🏦 Account numbersAccount No: 00123456789, Routing # 021000021, MRN, policy, IBANLabel-aware: only the value is hidden, the label stays readable.
🎂 Dates of birthDOB: 04/12/1988, Date of Birth March 3, 1990Only when labelled. Ordinary dates are left alone.
🔑 API keys & tokenssk-…, AKIA…, ghp_…, xoxb-…, JWTs, password: …Known vendor prefixes, plus key = value for passwords, secrets and tokens. Also catches keys whose underscores OCR read as spaces.

Faces

Faces are found with the SSD-MobileNet v1 model via TensorFlow.js (WebGL when available, CPU otherwise). Two details make it work on real-world images:

  • Letterboxing: each scan window is padded to a square so wide or tall images don't distort faces.
  • Tiling: images larger than 640 px are also scanned in overlapping 2×2 tiles (3×3 above 2,200 px) so small faces in group photos are found. Duplicates are merged with non-maximum suppression.

Faces are covered with an ellipse and extra margin. Minimum confidence is 0.55.

Barcodes & QR codes

HushShot uses the browser's native BarcodeDetector when present (Chrome on macOS, Android and ChromeOS). Elsewhere it falls back to ZXing in pure JavaScript, trying several scales (100%, 50%, 70%, 35%, 150%) because soft screenshot codes often decode only at a different size.

Supported: QR, Data Matrix, Aztec, PDF417, Code 128, Code 39, EAN-13/8, UPC-A/E, ITF, Codabar. For 1D codes, ZXing only reports a scan line, so the full bar height is estimated from the image. A structure check rejects text that was mistaken for bars.

06Metadata stripping

What is hiding in your file, shown before it's removed.

When a file opens, the Hidden metadata card lists what was found, colour-coded by risk:

FieldRiskWhy it matters
📍 GPS locationhighCan reveal your home or workplace to the metre.
🔢 Camera serial numberhighLinks photos across accounts to one device.
👤 Owner / authorhighYour name embedded in the file.
⛰️ GPS altitude, 📷 camera model, 🔭 lens serialmediumDevice fingerprinting.
🕒 Capture date, 💬 description, 🧭 unique image IDmediumTimeline and identity clues.
🛠️ Editing software, ©️ copyrightlowMinor, but still removed.
📄 PDF author, title, keywords, creator, producer, datesmed–highOften contain real names and internal document titles.

How removal works

  • Nothing is copied from the source file. The export is re-encoded from raw canvas pixels, so EXIF, XMP, IPTC, embedded thumbnails, ICC profiles and PDF info dictionaries have no way to come along.
  • PDFs are written with metadata auto-stamping off: no Producer, no creation or modification dates.
  • Verification: after writing, the app re-opens its own output and checks it for metadata. If anything is found, the save note turns amber and asks you to report the bug.
ℹ️

Dropping colour profiles can shift colours very slightly on wide-gamut photos. It's a side effect of removing every metadata block.

07Security & privacy model

Bad redaction is worse than none, so the design assumes someone will try to undo it.

Local-only processing

  • No backend exists. OCR (Tesseract.js), face detection (TensorFlow.js), barcode decoding (ZXing) and PDF rendering (PDF.js) all run in your tab.
  • All models, WebAssembly and fonts are bundled with the app. There are no CDN calls at runtime.
  • Content-Security-Policy in production builds: connect-src 'self', form-action 'none', object-src 'none', base-uri 'none'. Even a bug in the app couldn't send your file to another server.
  • Tested: the end-to-end suite fails if the page makes a single request to another origin or logs any CSP violation.
  • No analytics, cookies, accounts or telemetry.

Irreversible redaction

  1. Average first, smooth second. Blur and Pixelate first replace each block of pixels with its average colour, and only then smooth the result. The fine detail is discarded, not just obscured.
  2. Coarse cells over text. A line of text gets only about 1–2.4 cells across its height, larger than a glyph. Fine mosaics of a known font can be brute-forced back into text (the technique behind tools like Depix), and coarse cells defeat that.
  3. Random noise per block. Each block's colour is jittered, so an attacker can't pixelate candidate text and look for an exact colour match.
  4. Nothing is layered. The output is a fresh set of pixels. There is no original hidden underneath, unlike PDF highlight annotations, which can simply be deleted.
  5. PDFs are flattened. Each page is rasterised, redacted and re-embedded as an image, so the text under a redaction no longer exists in the file.
  6. Verified by OCR. The end-to-end test runs OCR on the exported file and asserts that none of the sample's secrets can be read, while normal text still can.
✅

For something extremely sensitive, choose Blackout. It replaces the area with a solid fill, so there is nothing left to analyse.

Threat model

ThreatMitigationStatus
File uploaded to a serverNo backend; CSP blocks outbound connectionscovered
Metadata leaks location / identityRe-encode from pixels; post-export verificationcovered
Hidden PDF text layer under a boxPages flattened to imagescovered
De-pixelation / de-blurringCoarse cells, per-block noise, averaging before smoothingcovered
Sensitive item not detectedHuman review, manual boxes, category togglesuser review
Sensitive info in the file nameNot changed beyond -redactedrename manually
Context clues (layout, logos, visible names)Out of scope for automatic detectionnot covered
Desktop wrapper reaching the networkLoopback-only server; every other request cancelled; path check keeps requests inside dist/covered

Found a way to recover redacted content or leak data? Please report it privately. See SECURITY.md, and only ever share fictional documents.

08Limitations

HushShot is a safety net, not a guarantee.

  • Names aren't detected. They depend on context and would cause many false positives with pattern matching alone. Use the drag-to-hide tool.
  • OCR isn't perfect. Tiny fonts, low contrast, handwriting, and rotated or curved text can be missed.
  • English-first. Address rules are US/Canada/UK oriented; OCR uses the English model.
  • Faces: heavily rotated, covered or tiny faces can be missed, and non-faces occasionally trigger.
  • Barcodes: without the native detector, damaged or very small 1D codes can be missed.
  • Not detected by design: license plates, signatures, logos, company names, free-form medical text.
  • PDFs are limited to 60 pages and become image-only.
  • Performance: the first scan loads about 15 MB of models. Face detection on CPU (no GPU acceleration) can be slow on large images.

09Architecture

A static single-page app: vanilla JavaScript, built with Vite, no framework.

Modules

FileResponsibility
index.htmlApp shell: landing page, workspace, SEO metadata
src/main.jsUI controller: state, scan pipeline, overlay interactions, export
src/detect/patterns.jsText rule engine. Pure JS, no DOM, unit-tested in Node
src/detect/textRegions.jsOCR words → character ranges → pixel boxes; wrapped lines; merging neighbours (pure)
src/detect/ocr.jsTesseract worker; upscales small screenshots, inverts dark themes, filters low-confidence noise
src/detect/faces.jsFace detection with letterboxing, tiling and NMS
src/detect/barcodes.jsNative BarcodeDetector or multi-scale ZXing fallback
src/render/effects.jsBlur, pixelate and blackout rendering
src/io/loader.jsImages and PDFs → canvases
src/io/metadata.jsReads metadata for display; verifies exports are clean
src/io/exporter.jsPNG / JPG / flattened-PDF writers, download and clipboard
desktop/main.cjsOptional Electron wrapper with a loopback-only server

Heavy modules (OCR, faces, barcodes, PDF, export) are loaded on demand with dynamic import(), so the landing page stays small. The OCR worker is reused across files and shut down when you click New file.

10Development

Scripts, extending detection, and the test suite.

npm scripts

CommandWhat it does
npm run devDev server with hot reload on port 5173
npm run buildVendors offline assets, then builds dist/ with the CSP injected
npm run previewServes the production build on port 4173
npm testUnit tests (rules, box mapping, metadata, redaction cell size), under a second
npm run e2e -- <url>Full pipeline in headless Chromium against a running build
npm run samplesRegenerates the fictional sample documents
npm run examplesRegenerates the before/after gallery in docs/examples/
npm run desktop / desktop:packLaunch or package the Electron wrapper

Adding a detection rule

A rule is one object in the RULES array in src/detect/patterns.js:

{
  // Example: an employee ID like "EMP-004521"
  type: 'account',                          // category id (see CATEGORIES)
  re: /\bEmployee\s*ID\s*[:#]?\s*(EMP-\d{6})\b/gi, // must use the g flag
  group: 1,                                  // optional: hide only this capture group
  confidence: 0.9,                           // or validate(match) → 0..1 (0 = reject)
}
  1. Add the rule object. Use validate() for checks a regex can't do, like a checksum.
  2. For a new category, add it to CATEGORIES and TEXT_TYPES in patterns.js, and to TAGS and CAT_ORDER in src/main.js.
  3. Add positive and negative cases to tests/patterns.test.js. False positives that hide harmless text are bugs too.
test('employee id', () => {
  assert.equal(found('Employee ID: EMP-004521', ['account'])[0].s, 'EMP-004521');
  assert.deepEqual(found('EMP-004521 was mentioned', ['account']), []);  // no label → no match
});

Testing

$ npm test                                  # unit tests
$ npm run build && npm run preview &
$ npm run e2e -- http://localhost:4173/     # end-to-end

The end-to-end test drives the real UI against the production build, so the CSP is enforced. It asserts:

  • every category is detected on the sample pay stub;
  • OCR of the exported PNG can't read any secret, while normal text stays readable;
  • a JPEG with GPS and camera data comes out with no EXIF segment;
  • a 2-page PDF with a text layer and author metadata comes out with no selectable secrets and an empty info dictionary;
  • zero external requests, and zero console errors or CSP violations.

CI (.github/workflows/ci.yml) runs unit tests, the build and the end-to-end test on every push and pull request.

Debugging

Open the app with ?debug (e.g. http://localhost:5173/?debug) to expose window.__hushshot in the console. It contains the live state (pages, OCR lines, regions) plus detectFaces() and detectBarcodes() for trying detectors on any canvas. Detector failures are logged with a [hushshot] prefix.

11Configuration

Build-time variables and tuning constants.

SettingWhereDefaultPurpose
VITE_BASEenv var./Public base path. The Pages workflow sets it to /<repo>/.
VITE_SITE_URL.envPages URLAbsolute URL for canonical and Open Graph tags. Change it when you host your own copy.
PDF_SCALEio/loader.js2 (144 dpi)PDF render resolution: sharper text versus memory use.
MAX_PDF_PAGESio/loader.js60Page limit for PDFs.
MAX_SIDEio/loader.js9000 pxLarger images are scaled down.
minConfidencedetect/faces.js0.55Face detection threshold.
Word confidence filterdetect/ocr.js45Low-confidence OCR words are dropped unless they contain a digit or @.
cellSize()render/effects.jssee codeMosaic coarseness per region type. Unit-tested to stay coarse for text.

12Deployment

It's a folder of static files. Host it anywhere.

GitHub Pages (built in)

  1. Fork the repository.
  2. Go to Settings → Pages and set Source to GitHub Actions.
  3. Set VITE_SITE_URL in .env to your Pages URL, and update public/robots.txt and public/sitemap.xml.
  4. Push to main. .github/workflows/pages.yml builds and publishes automatically.

Any static host

$ npm ci && npm run build   # upload the dist/ folder

For hosts that let you set HTTP headers (Netlify, Cloudflare Pages, nginx), consider adding the same CSP as a real header plus frame-ancestors 'none', which can't be set from a <meta> tag. Serve .wasm as application/wasm.

Desktop installers

npm run desktop:pack produces a DMG (macOS), an NSIS installer (Windows) or an AppImage (Linux) in release/, using desktop/electron-builder.yml.


13Troubleshooting

SymptomCause & fix
"HEIC photos aren't supported"Browsers can't decode HEIC. Convert to JPG, or set iPhone camera format to Most Compatible.
First scan is slowAbout 15 MB of models load on first use and are reused afterwards. Face detection without GPU acceleration runs on the CPU. Enable hardware acceleration in your browser settings.
A step shows "unavailable"That detector failed to load (often offline on a first visit, or WebGL blocked). The rest still works, and you can draw boxes by hand. Reload once online.
"Copy to clipboard" blockedSome browsers restrict image clipboard writes. Use Save instead.
Something wasn't detectedDrag a box over it. If it's a common format, open an issue with a fictional example.
Harmless text got hiddenClick the box to reveal it, or switch the category off.
Exported PDF isn't searchableExpected: pages are flattened so hidden text can't survive.
npm test fails with "Cannot find module …/tests"Old script on Node 22+. Pull the latest main, which uses node --test tests/*.test.js.
OCR / face assets 404 in devRun npm install (or node scripts/copy-assets.mjs) to vendor them into public/vendor.

14FAQ

Does anything get uploaded?

No. There's no backend. Open your browser's Network tab: after the page loads, dropping a file causes zero requests.

Is it free? Do I need an account?

Free and open source under the MIT license. No account, no sign-up, no limits.

Why not just blur it in Preview or Paint?

You'd have to spot every item yourself and get each box right, and the file's EXIF/GPS data would still be there. Many blur tools are also too fine to be safe for text. HushShot does the spotting, the safe redaction and the scrubbing.

Does PNG export change my image?

PNG is lossless outside the redacted areas. JPG is re-encoded at quality 93. Colour profiles are dropped as part of metadata removal.

Can I add my own patterns?

Yes. It's one object in patterns.js; see Adding a detection rule. A UI for custom rules is on the roadmap.

Can I use it on sensitive work documents?

Yes, that's what it's for, and nothing leaves your device. Still review the preview, and use Blackout for the highest assurance.

Does it work on phones?

Yes, in modern mobile browsers. Large images and face detection are slower on phones.

15Roadmap

🧠 Name detection

Optional on-device named-entity model for names and organisations.

📂 Batch mode

Drop a folder, get a folder back.

🌍 More locales

EU addresses and national IDs, IBAN validation, non-Latin OCR.

🚗 More detectors

License plates and signatures.

📄 Selectable PDFs

True content-stream redaction that keeps text searchable.

🧩 Browser extension

Right-click any image → HushShot.

📲 Installable PWA

Web manifest ships already; offline service worker next.

✍️ Custom rules

Bring your own regex or keywords from the UI.

16Contributing & license

Contributions are welcome: new detection rules, locale support, UI polish, and above all bug reports with fictional reproductions. Ground rules:

  • No network calls. Ever. Bundle assets instead of loading them from a CDN.
  • Never keep original pixels or metadata in the output. Exports must be re-encoded from a canvas.
  • Only fictional data in tests and samples.
  • Run npm test and the end-to-end test before opening a pull request.

Credits

Built on Tesseract.js, @vladmandic/face-api + TensorFlow.js, ZXing, PDF.js, pdf-lib, exifr and Vite.

License

MIT © HushShot contributors.