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

certifipic

v1.0.0

Published

CertifiPic by Faraz Rahimi — image authenticity engine, SDK, and HTTP API.

Readme

CertifiPic

CertifiPic by Faraz Rahimi

Image authenticity lab and full API: localize edits on a photo, score how AI-generated it looks, and return JSON + PNG overlays you can drop into any app.

Copyright (c) 2026 Faraz Rahimi. Use it, study it, duplicate it, and call it from your own apps. The app UI does not need to mention CertifiPic or Faraz Rahimi. If the app is publicly documented (GitHub, docs, a paper), copy LICENSE exactly (including every co-author named in it) and cite this repo. Forks of the source must keep that exact license and credit.


Quick start

Library name: certifipic. Install from GitHub (this works today):

npm install github:FarazRahimi/CertifiPic

npm install certifipic will work only after the package is published to npmjs.com. It is not on the registry yet.

import { analyze } from "certifipic";

const result = await analyze(file, { colors: { ela: "#ff8800" } });

Run the lab and HTTP API from this repo:

git clone https://github.com/FarazRahimi/CertifiPic.git
cd CertifiPic
npm install
npm run dev          # lab UI  → http://localhost:5173
npm run api          # HTTP API → http://127.0.0.1:8787

Analyze from any language:

curl -F [email protected] http://127.0.0.1:8787/v1/analyze

Customize mark colors:

curl -F [email protected] -F options='{"colors":{"ela":"#ff8800"}}' http://127.0.0.1:8787/v1/analyze

Full request/response reference: docs/API.md.


What it is

CertifiPic is not a single “fake / real” neural net. It is a pipeline that:

  1. Reads file provenance (C2PA / IPTC / XMP / PNG text / JPEG comments).
  2. Classifies the raster as photograph, document (ink on paper), or graphic.
  3. Runs classic forensic maps: ELA, noise residual, edges, copy-move.
  4. On photos, runs supporting Fourier and sensor-grain screens.
  5. Fuses those into a risk score, regions, colored marks, and an AI-generation score (0–100) in the sidebar / API — not as an AI heatmap on the pixels.

The test webpage is a client. The engine lives in src/engine/. The public SDK is src/sdk.ts. The HTTP server is api/server.ts (same engine).


How the logic works

Algorithm map — every image walks this graph:

flowchart TD
  A[Image bytes] --> B[Decode and scale<br/>maxDimension 1600]
  B --> C[Provenance<br/>C2PA / IPTC / XMP / EXIF]
  C --> D{File AI label?}
  D -->|trainedAlgorithmicMedia| E[AI score ~98<br/>kind: full]
  D -->|composite / generative fill| F[AI score high<br/>kind: partial]
  D -->|no label| G[Classify type]

  G --> H{photograph / document / graphic}
  H -->|document| I[Paper-baseline ELA<br/>ignore letter strokes]
  I --> J[Compact edit blob only]
  J --> K[AI screens off]
  K --> Z[JSON + PNG marks<br/>AI score in sidebar]

  H -->|photograph or graphic| L[Source profile<br/>camera / resave / screenshot]
  L --> M[ELA]
  L --> N[Noise residual]
  L --> O[Edges]
  L --> P[Clone / copy-move]
  L --> Q[Fourier FFT]
  L --> R[Sensor grain]
  M --> S[Fusion]
  N --> S
  O --> S
  P --> S
  Q --> T[AI score 0–100<br/>no AI heatmap]
  R --> T
  S --> U[Risk / verdict / regions]
  T --> U
  U --> V[Color washes<br/>ela / noise / clone]
  V --> Z
  E --> Z
  F --> V

Document vs photo (why invoices are not treated like selfies):

flowchart LR
  subgraph Photo
    P1[ELA + noise + clone] --> P2[FFT + sensor]
    P2 --> P3[AI score + edit marks]
  end
  subgraph Document
    D1[Ink-on-paper check] --> D2[ELA vs paper only]
    D2 --> D3[Mark the pasted patch<br/>not the whole page]
  end

Then the stages in order:

1. Decode and size

The file is decoded to an ImageBitmap and scaled so the longest side is maxDimension (default 1600). All maps share that raster. EXIF orientation is respected when the decoder provides it.

2. Provenance (first, when it exists)

src/engine/provenance.ts and src/engine/metadata.ts:

  • EXIF / XMP via exifr (camera make/model, software, timestamps, GPS, embedded thumbnail).
  • JPEG COM comments and PNG tEXt.
  • IPTC Digital Source Type strings:
    • trainedAlgorithmicMedia → fully AI (file label)
    • compositeWithTrainedAlgorithmicMedia → partly AI (file label)
  • C2PA four-byte marker c2pa in the file header/trailer (presence only; signatures are not cryptographically verified in this build).

Generator names (midjourney, dall-e, …) are matched in metadata and filename only, not in JPEG entropy bytes (that caused false hits).

If the file still carries a real AI label, that wins. Pixel heuristics do not override a trainedAlgorithmicMedia stamp.

Many chat exports and screenshots strip these tags. Then the engine falls back to pixels.

3. Image type

src/engine/fusion.ts → classifyKind:

  • Document: pale paper + short dark ink runs (letters), not just a bright room. Filename hints (invoice, receipt, …) help.
  • Graphic: very low contrast / UI-like.
  • Photograph: everything else.

Documents turn off clothing/FFT AI screens (type grids dominate a Fourier transform) and use a paper-baseline ELA: ignore letter strokes, keep compact compression blobs (a pasted dollar amount), not the whole page.

4. Source profile

src/engine/source.ts decides how much to trust each detector:

| Source | ELA | Notes | |---|---|---| | Camera JPEG with EXIF | Trusted | Best case | | Re-encoded / social JPEG | Weak | Double compression everywhere | | PNG / screenshot / clipboard | Not trusted | ELA lights up UI text; clone-search is more useful |

5. Error Level Analysis (ELA)

src/engine/ela.ts

Re-save the raster as JPEG at elaQuality (default 0.75) and take a per-pixel difference from the original. Patches that were already dirty, pasted, or saved at a different quality stand out.

Marks (src/engine/marks.ts) do not dump the raw ELA map on the photo. They isolate compact blobs relative to a local/paper baseline and paint a wash in colors.ela (default amber #e3b34d). You can change that hex in the API.

6. Noise residual

src/engine/noise.ts

High-pass residual vs a blur. Tile coefficient of variation flags splices where grain does not match. Hair/fabric with high contrast and grain is skipped so it does not look like a fake. Wash color: colors.noise.

7. Edges

src/engine/edges.ts

Sobel-style gradient magnitude. Documents are expected to be edge-heavy (type). Photos with wild spatial CV get a mild score. This is a supporting signal, not a sole verdict.

8. Clone / copy-move

src/engine/clone.ts

Block matching on a downscaled grid. A cluster of pairs with the same shift vector is a clone stamp. Repeating bricks/checkerboards are discarded when the pair list explodes. Two washes: source (clone-src, mint) and destination (clone-dst, red). Off on documents (repeated letters are not copy-move).

9. Fourier + sensor (photos only)

  • src/engine/spectrum.ts — 2D FFT. Grid / peakiness vs a natural 1/f slope. Buildings and type look “grid-like”; that is why this is off on documents and is not painted as an AI heatmap.
  • src/engine/sensor.ts — residual smoothness (denoise / synthetic fields). Not a true PRNU camera fingerprint.

These numbers feed the AI score. They do not stripe the picture.

10. AI-generation score

src/engine/ai.ts

| ai.kind | ai.score | When | |---|---|---| | full | ~90–98 | File label, or (rare) whole-frame stack without a camera | | partial | ~52–88 | File composite label, or a concentrated repeating/smooth patch (not the whole frame) | | none | 0 | Default. Clean photos stay at 0 |

The product shows this as how AI generated (sidebar meter / ai.score). There is no AI heatmap overlay. Fully labeled AI still gets a FULLY AI GENERATED stamp on the lab canvas.

Invisible vendor watermarks are not read here and are not mentioned in the user-facing report.

11. Fusion and report

src/engine/fusion.ts weights ELA / noise / edges / clone / metadata by source type.

src/engine/report.ts writes a short plain-language note from those scores.


Repository layout

src/engine/     forensic pipeline (ELA, noise, clone, AI, fusion)
src/sdk.ts      public JS/TS API (analyze + options)
src/App.tsx     test lab UI
api/server.ts   HTTP API (same engine, Node + canvas)
api/polyfill.ts OffscreenCanvas for Node
backend/        optional FastAPI sidecar (not the full engine)
docs/API.md     HTTP + SDK reference
LICENSE         CertifiPic License
NOTICE          Exact LICENSE copy; fork credit
CONTRIBUTING.md Inbound contributions use the same license

Scripts

| Command | What | |---|---| | npm run dev | Vite lab on port 5173 | | npm run api | HTTP API on port 8787 | | npm run build | Typecheck + production lab build | | npm run preview | Serve the production lab |

Optional Python sidecar:

cd backend
pip install -r requirements.txt
python main.py          # http://127.0.0.1:8000

License

CertifiPic License — see LICENSE and NOTICE.

  • App UI: you do not have to show CertifiPic or Faraz Rahimi inside the product.
  • Public docs / GitHub of a project that uses it: copy LICENSE exactly (verbatim, including every co-author named in it) and link to https://github.com/FarazRahimi/CertifiPic. Do not abridge or rewrite the license.
  • Fork or publish changed source: keep an exact copy of this license (all co-author names), keep NOTICE, keep CertifiPic by Faraz Rahimi, and keep the repo link.
  • Source copies: leave copyright headers, co-author names, and LICENSE in place.
  • Name: do not call your product CertifiPic or claim Faraz Rahimi endorses it.
  • Contributions: come in under the same license. No warranty. This license does not claim any patent.