ItsPaint

August 26, 2026 · View on GitHub

ItsPaint

MS Paint for the Mac. Paste a screenshot, number the steps, drag it out.

Preview markup has no step badges, no pixelate and no drag-out.

3.46 MB · free on the App Store · MIT · no network entitlement, so the kernel refuses a socket

Release Mac App Store MIT tests

brew install --cask joshlin2201/itspaint/itspaint

Download the disk image · Mac App Store · macOS 14+, universal

Pasting a settings sheet, numbering three steps with badges, and pixelating an API token, in nine seconds

What makes it different

  • 3.46 MB, and a window on screen in half a second. Krita is a gigabyte and built for painters. CleanShot X is $29 plus a cloud subscription. Preview is already on your Mac and will not number a step or pixelate a token.
  • No network entitlement. com.apple.security.network.client is not requested, so the kernel will not open a socket for it. Three commands to check that yourself.
  • Background removal with no ML model and nothing to download. Four corner-seeded flood selections, unioned. Thirty lines of code.
  • The drawing engine has no UI. Packages/PaintKit imports Foundation, CoreGraphics, ImageIO, CoreText and UniformTypeIdentifiers, and nothing else, so swift test verifies most changes with no Xcode. It is also a package you can depend on.
  • Free, MIT, and it stays that way. There is no account and no telemetry.

Paste the screenshot, number the steps, crop it, drag it out, close the window. Nothing lands on your Desktop.

PaintKit as a dependency

The engine is its own SwiftPM product, so you can draw, key out a background or encode a PNG with none of the app around it. No AppKit, no third-party dependency, macOS 12 and up.

.package(url: "https://github.com/joshlin2201/itspaint", from: "0.19.0")

Swift versions Platforms

Those two are served from the Swift Package Index's own API rather than written here, so they report what it built, on a machine that is not mine, and they go red without anybody having to notice. The same page records zero data-race-safety errors — every target in both manifests is swiftLanguageMode(.v6), so strict concurrency is on and nothing is waived.

The macOS 12 is deliberate. The app needs macOS 14; the engine does not and no longer says it does. It had the app's number by inheritance rather than by need, and a floor is not a warning a consumer can ignore — SwiftPM refuses to resolve at all when a dependency's deployment target is above yours.

PaintKit gets you a product shot off its page in four lines, on device, with no model and no network:

import PaintKit

let engine = PaintEngine(canvas: try ImageCodec.decode(contentsOf: input))
guard engine.removeBackground() else { fatalError("no background to remove") }
try ImageCodec.encode(engine.canvas, as: .png).write(to: output)

Those four lines are compiled on every push, by a CI job that builds them from a package outside this repository — the one that declares macOS 12, so the floor above cannot quietly rise. And the result is checked by counting transparent pixels rather than by whether the program exited quietly: on a 60px mark centred on a 400×400 page the count goes from 0 to 156,400, which is 160,000 minus the 3,600 pixels of subject, asserted for six subject sizes.

If you do depend on it: it ships often while it is in beta, and until 1.0 a minor version may still carry a breaking change to the document format — CHANGELOG.md says so at the top. Watch ▸ Custom ▸ Releases is the notification for that one thing. Watching the repository outright also mails you every issue and every comment, which is why most people do not.

What it does

Marking up

Step badges1, 2, 3… auto-numbered, numeral auto-contrasted, and the sequence can start anywhere for a run that spans two screenshots.
Arrows and calloutsA draws an arrow. Fifteen shapes in all, solid, dashed or dotted, outlined or filled.
HighlighterIts own ink, kept apart from the colour pair. Overlapping passes in one stroke never darken.
PixelateA mosaic over the part you would rather not publish. Block size 4–48.
SpotlightDrag a box and everything outside it dims, so the eye lands where you meant it to.
Text with a rimOne colour cannot stay legible across a light panel and a dark one, so annotation text carries a contrasting edge.

Getting things in and out

Drag straight outInto any app that takes an image. No save panel, and no Screenshot 2026-08-07 at 11.42.13.png left behind.
Paste onto anythingIt arrives floating. Bigger than the canvas and the canvas grows rather than cropping it.
Nine export formatsPNG, JPEG, TIFF, BMP, GIF, HEIC, AVIF, PDF, ICO. Formats without alpha flatten onto Colour 2, not onto black.
Sign a PDFOpen a contract, sign the page, save it back as a PDF. The page stays the size it was printed at, and the pages you did not touch keep their text.
Open WithRegistered for PNG, JPEG, TIFF, BMP, GIF, HEIC and PDF.

Remove a background with no model and no network

A product shot on a flat page, then the same window with the background gone and the checkerboard showing through

One command. It floods from all four corners and makes the union transparent. If the page is not actually separable it says so and changes nothing.

It knows what a page is, not what a subject is, so it works on logos, product shots and scanned diagrams. It is useless on hair.

How it works, in thirty lines of code →

Painting

Thirteen toolsPencil, brush, highlighter, eraser, clone, shape, text, badge, fill, eyedropper, select, pixelate, spotlight. One rail button each, every one a single key.
Four brush nibsRound, square, soft, and spray. Spray keeps spraying while you hold still, which is the whole feel of an airbrush.
Four ways to selectRectangle, ellipse, lasso, and Instant Alpha. Marching ants follow the pixel mask rather than its bounds.
Pixel controlPointer-centred zoom, nearest-neighbour above 100%, a pixel grid, live tool footprints, rotate by any angle.
Snap to grid⇧⌘' at 8–64px for shapes, selections and pasted content. Freehand ignores it.
ItsPaint editing a chameleon painting on a transparent canvas, with the brush options open

Thirteen tools, fifteen shapes, and a transparent canvas. The window above is ItsPaint painting, not marking up.

Install

brew install --cask joshlin2201/itspaint/itspaint

Or take the disk image, open it and drag ItsPaint to Applications. It is also free on the Mac App Store.

It ships often while it is in beta. Watch ▸ Custom ▸ Releases is the quietest way to hear about a new build.

RequiresmacOS 14 Sonoma or later
Architectureuniversal, one build for Apple silicon and Intel
SigningDeveloper ID, notarised, ticket stapled to the image and the app, so the check needs no network and there is nothing to clear from the Terminal
Download3.46 MB for the disk image, with a SHA-256 in checksums.txt
shasum -a 256 -c checksums.txt
xcrun stapler validate ItsPaint-*.dmg

If macOS asks you to confirm the first launch, open System Settings ▸ Privacy & Security and click Open Anyway. Why that happens.

No network. Three commands to prove it

This is a property the kernel enforces, not a promise anyone is making. The first two commands run against the copy in /Applications, the third greps this repository:

codesign -d --entitlements - --xml /Applications/ItsPaint.app | plutil -p -
otool -L /Applications/ItsPaint.app/Contents/MacOS/ItsPaint
grep -rniE 'URLSession|NWConnection|import Network|CFSocket' App Packages
CommandWhat comes back
codesignThree entitlements: sandbox, files you pick in a panel, app-scoped bookmarks. No network.client and no .server, so the kernel will not open a socket for it
otoolApple frameworks and the Swift runtime, on both architectures. No CFNetwork, no Network.framework, no bundled dylib
grepNothing. Package.swift also declares no dependency, so there is no third-party code behind it

The entitlements check goes first because it is the one that holds even if the developer is lying to you. It reports what the kernel will permit.

Why that order, and how to make the same claim falsifiable in your own app →

Contributing, and the engine needs no Xcode

Because PaintKit has no UI, most changes to how ItsPaint draws can be made and checked with a text editor and a terminal. No simulator, no GUI session, no Xcode project:

git clone https://github.com/joshlin2201/itspaint.git
cd itspaint
swift test          # the whole engine suite, in a fresh clone

Seven issues are open and labelled good first issue, each naming the file and the line to start at. They sit in three different parts of the app so you can pick whichever suits you:

View layer, AppKit drawing, no engine knowledge needed

Engine, pure functions over pixels, no Xcode needed

Selection arithmetic, also engine-only

CONTRIBUTING.md covers the structure, the design constraints, and what a change has to prove before it lands.


Tools and shortcuts
GroupTools
DrawPencil, Brush (round / square / soft / spray), Highlighter (own ink, four colours), Eraser, Clone (clone / soften)
InsertShape, Text, Step Badge, Fill, Eyedropper
SelectRectangle, Ellipse, Lasso, Instant Alpha
EffectsPixelate, Spotlight

Thirteen rail buttons. Variations live inside the tool that owns them, so there are fifteen shapes behind Shape, four nibs behind Brush and four modes behind Select. The whole set stays available without a button each.

ShortcutActionShortcutAction
PPencilBBrush
HHighlighterEEraser
CCloneFSoften
UShapeTText
NStep BadgeKFill
IEyedropperMSelect
RPixelateSSpotlight
XSwap colours[ / ]Change tool size
SpacePanEscCancel what you are doing
⌥1⌥9Choose a shape⇧⌘'Snap to grid
⌘KCrop to selection⌘9Fit to window
⇧⌘EExport⌘VPaste as a floating image

Pinch or -scroll to zoom around the pointer. Hold to sample a colour without changing tools. Right-drag uses the second colour. Esc cancels the current shape, text box, selection, floating paste, or options panel.

Instant Alpha selects connected pixels by colour. -click adds, -click subtracts, then Make transparent.

Pixelate is for visual de-emphasis and not for secure redaction. Cover private information with an opaque filled shape before you export a flattened image.

Files and export

ItsPaint documents use the .itspaint package format, which is a lossless PNG with JSON metadata for the canvas, colours and palette.

Export supports PNG, JPEG, TIFF, BMP, GIF, HEIC, AVIF, PDF and ICO, when the corresponding encoder ships with the installed macOS version. The export panel has format and scale controls, and formats without alpha are flattened onto the second colour.

A PDF opens as a page: ItsPaint rasterises the page you are working on at 144 dpi so you can draw on it, and keeps the file it came from. Saving writes a real PDF — the edited page at its original size in points, and every other page copied through with its text intact. Multi-page documents get a page control at the bottom of the window, and turning a page keeps what you drew on the last one.

ItsPaint registers as an editor for PNG, JPEG, TIFF, BMP, GIF, HEIC and PDF, so it appears under right-click ▸ Open With. It asks for Alternate rank on PDF rather than owning the type, because Preview should stay the Mac's PDF reader.

WebP export is not available, because macOS does not provide a WebP encoder. Text becomes pixels once it is committed.

How it is built
Packages/PaintKit/   UI-free drawing engine, raster operations, undo, and codecs
App/                 AppKit document lifecycle, canvas, and SwiftUI interface

PaintKit stores pixels as premultiplied RGBA8 and returns the changed rectangle from every edit. The canvas redraws only that area, and undo history is bounded by memory rather than by a fixed number of steps.

Background removal without a model is a worked example of both layers. Four corner-seeded flood selections unioned through the same combiner -click uses, and a guard that declines rather than returning a nearly blank canvas.

That guard was also wrong for three releases, in a way worth reading about: a guard tuned to its test →

PaintKit is a product of the root package, so it can be a dependency of anything that wants a raster canvas without an editor around it. The four-line example is in Contributing above.

docs/README.md has the design notes, architecture, feature reference, testing guide and roadmap. CHANGELOG.md has the version history.

First launch, and the Gatekeeper message

Open System Settings ▸ Privacy & Security and click Open Anyway.

This happened once, on macOS 26.6, with a freshly downloaded 0.12.0, on a disk image that spctl --assess accepts and whose stapled ticket validates. It looks like a first-launch check that went to Apple and did not come back. It is alarming enough when it happens that it belongs here rather than in an issue.

Check the download yourself:

shasum -a 256 -c checksums.txt
codesign --verify --deep --strict --verbose=2 /Volumes/ItsPaint*/ItsPaint.app
spctl -a -vvv -t exec /Volumes/ItsPaint*/ItsPaint.app   # expect: accepted

If you installed it, a star is how the next person finds it from search.

MIT License · Built by Josh Lin