← Back
henrydennis

henrydennis/petal

A fast, exact disk-space explorer for macOS with a live sunburst chart. Rust + GPUI.

View on GitHub ↗
apfsdisk-spacedisk-usagegpuimacosruststoragesunburst
Stars
30
Forks
2
Watchers
30
Open issues
5
Contributors
1
Language
Rust
License
MIT License
Default branch
main
Created Oct 1, 2026Updated Oct 1, 2026

Star growth

Today—
This week—
This month—

Star history will appear here once this repo has been tracked for a couple of days.

README

Petal icon

Petal

See what's filling your Mac. In seconds.
A fast, exact disk-space explorer for macOS with a live sunburst chart.
Free and open source, written in Rust with a GPU-rendered native UI.

CI macOS 13+ Rust 1.95+ MIT license

Petal in 50 seconds: play the video
▶ Watch Petal in 50 seconds (with sound)


Petal shows your disk as a sunburst: the centre is the folder you're looking at, each ring is one level deeper, and the size of a slice is how much space it takes. It draws the picture while it reads, tells you which big folders are safe to clear, and tells you exactly how much deleting them will free.

Highlights

  • Live from the first second. The chart appears straight away and fills in as Petal reads. Folders turn from muted to full colour the moment their total is final, and you can click into them before the scan has finished. Segments glide into place instead of jumping.
  • Fast. A whole Mac (about 6 million files) in roughly 25 seconds; a typical home folder in a few seconds; a 90,000-item folder in under a second. Directory listings use getattrlistbulk and openat across all cores. The performance notes have the measurements.
  • Exact. Sizes are allocated blocks, so they match Finder's "on disk". For the startup disk, every other APFS volume (macOS itself, Preboot, VM, Recovery…) gets an exact slice, so the chart adds up to the disk's used space to the byte.
  • Findings. Petal checks the usual suspects first (Trash, Downloads, Xcode build files and archives, iOS device support and simulators, iPhone backups, Docker, app caches, npm, Cargo, Gradle, Movies, Mail, node_modules, Chrome's update leftovers) and labels each Safe to delete or Review first, with a one-line explanation. The first ones show up within a fraction of a second.
  • Savings you can trust. On APFS, deleting a cloned or hard-linked file may free nothing. Petal works out what deleting your selection really frees, counting clones and hard links once, and this is tested against real deletions on a throwaway APFS volume.
  • Collect, then clean up. Drag folders (or press +) into the Collector, check the total, and move them to the Trash in one go. Nothing is deleted outright; you can put things back from the Trash.
  • Safe by design. Petal only reads file metadata. It never downloads iCloud files that are only in the cloud, never sends anything over the network, and has no analytics.

Screenshots

Results with findings Scanning, with the chart filling in live
Results. Findings, folder list and the full chart. Scanning. Final folders in colour, the rest still counting.
Zoomed into Library, hovering Caches Collector with two items
Explore. Click a slice to zoom in; hover for sizes. Collector. What deleting your selection frees, exactly.

A live scan, slowed down 8×
A real scan of a sample home folder, slowed down 8×.

All screenshots and the video use a made-up home folder generated by scripts/make-demo-home.py, not anyone's real files.

Install

Download

Download Petal 0.1.0 (DMG, 5.5 MB) for Macs with Apple silicon (M1 or later) running macOS 13 or later.

  1. Open the DMG and drag Petal into Applications.
  2. Open Petal. This build isn't notarized by Apple yet, so macOS says it can't verify the app. Click Done, then open System Settings › Privacy & Security, scroll down and click Open Anyway next to the message about Petal. You only need to do this once.

All releases are on the Releases page.

Build from source

It takes a couple of minutes. You need: macOS 13 or later, the Xcode Command Line Tools (xcode-select --install), and Rust. The repository pins the toolchain in rust-toolchain.toml, so rustup fetches the right version automatically.

git clone https://github.com/henrydennis/petal.git
cd petal && scripts/bundle.sh

That builds dist/Petal.app (signed ad hoc, so it runs on the Mac that built it). Drag it into Applications and open it. scripts/bundle.sh --dmg also makes a disk image.

Or run it straight from the source tree:

cargo run --release

Full Disk Access

macOS keeps some folders private (Mail, Messages, Safari, other apps' data) unless an app has Full Disk Access. Petal works without it, but shows exactly how much it couldn't read, and offers a button that opens System Settings › Privacy & Security › Full Disk Access. Turn Petal on there; the card in Petal notices within a couple of seconds and offers to rescan.

When you run Petal from a terminal (cargo run), macOS asks about the terminal app instead of Petal.

Using Petal

The first time you open Petal it starts scanning your startup disk straight away. After that it opens on the Disks screen, with your volumes, Scan Home Folder and Choose Folder….

  • Hover a slice or a row to see its size; the two stay in sync.
  • Click a folder (in the chart or the list) to zoom in. Click the centre, or use the breadcrumbs, to go back up.
  • Click a finding to open its folder in the chart; press + on it to collect it.
  • In the list, ⌕ reveals an item in Finder and + adds it to the Collector.
  • Move to Trash… asks for confirmation, moves the collected items to the Trash, and updates every total in place without rescanning.
Keys Action
⌘O Choose a folder to scan
⌘R Rescan
⌫, Esc or ⌘↑ Go to the enclosing folder
⇧⌘D Back to the Disks screen
⌘Q Quit

You can also pass a folder on the command line: petal ~/Library.

How it works

src/
├── main.rs        app setup, menus, key bindings, command-line flags
├── app.rs         the GPUI view: disks, scanning and results screens, sidebar, findings, collector
├── scan.rs        parallel walk (outline → hotspots → depth-first), the flat Tree, volumes, frees_of
├── dirlist.rs     getattrlistbulk/openat directory listing, with a portable fallback
├── live.rs        live per-folder totals during a scan, snapshots, and the live-chart benchmark
├── disk.rs        the startup disk's APFS container: exact slices for the other volumes
├── findings.rs    the catalog of known space hogs and how to recognise them
├── sunburst.rs    layout in angle space, painting with PathBuilder, polar hit-testing, colours
├── motion.rs      easing segments between live snapshots
├── eta.rs         progress (items vs. the volume's object count) and "about N s left"
├── onboarding.rs  first run and Full Disk Access detection
├── clock.rs       the UI clock (lets the recorder pause time)
└── snapshot.rs    dev tool: scripted input, screenshots and video recording
  1. Outline. The first two levels are listed by name in ~10 ms, so the chart has a shape at once.
  2. Hotspots. Known space hogs from the findings catalog are read next, in parallel, so findings and the biggest slices are exact within a couple of seconds even on a full disk.
  3. Main walk. Everything else, depth-first across all cores, reusing the hotspot results. Every folder in the top four levels has an atomic running total, and the UI snapshots those ten times a second. A folder is marked final as soon as the walk leaves it.
  4. Results. The finished tree is a flat array of nodes. Findings resolve their exact savings in the background (scan::frees_of), and results never wait for them.

The UI is built with GPUI (the community edition of the framework behind the Zed editor), which renders everything on the GPU with Metal. The chart is painted with PathBuilder inside a canvas, and the folder list is a virtualised uniform_list.

Development

cargo test                          # unit tests
cargo test -- --ignored             # plus the APFS clone-accounting test (creates and deletes a small disk image)
cargo run --release -- --bench-scan ~/Library 6   # headless scan benchmark (median of 6)
cargo run --release -- --bench-live ~/Library 3   # how soon the live chart looks right
cargo run --release -- --check-access             # does this process have Full Disk Access?

bench/ab.sh runs two builds alternately and compares their medians, and refuses to report a result if the two scans disagree. See docs/PERFORMANCE.md for the method and the history of what was tried.

Screenshots and video without Screen Recording permission

With --features snapshot, Petal can drive its own window from a script and save PNGs, or record a video (needs ffmpeg). While recording, the app's clock and the scan pause during each frame's capture, so the video plays at the app's real speed and a smooth 30 fps no matter how long each capture takes. slow-motion N records the following steps N times slower, still at 30 fps.

scripts/make-demo-home.py /tmp/demo
HOME=/tmp/demo/alex PETAL_HIDE_ACCESS=1 PETAL_VIDEO=/tmp/petal.mp4 \
PETAL_SCRIPT="hide-pointer; until-done 10; wait 1; show-pointer; move 1100 700; glide 904 363 1; click; wait 2; quit" \
cargo run --release --features snapshot -- /tmp/demo/alex

Script steps: wait S, glide X Y S, move X Y, click, until-done MAX, slow-motion N, hide-pointer, show-pointer, quit (coordinates are in points within the window). For PNGs, set PETAL_SNAPSHOT=/tmp/petal instead and use shot NAME and burst NAME COUNT MS.

Packaging for other Macs

scripts/bundle.sh signs ad hoc by default, which is fine on the Mac that built it. To distribute, sign with a Developer ID and notarize:

PETAL_SIGN_ID="Developer ID Application: Your Name (TEAMID)" PETAL_NOTARY_PROFILE=petal-notary scripts/bundle.sh --dmg

(Save the notary profile once with xcrun notarytool store-credentials petal-notary.) macOS ties Full Disk Access to the code signature, so a stable Developer ID signature also keeps access granted across updates.

Credits

  • Inspired by DaisyDisk, which pioneered the sunburst disk explorer on the Mac. Petal is an independent project, not affiliated with it.
  • Built on GPUI CE, rayon, palette and trash.
  • The promo video was edited in Tesseract with motion graphics made in Remotion, and narration and music from ElevenLabs.

License

MIT