Share a speedscope profile
speedscope has the best flame chart interaction of any of the free profile viewers — the time-ordered view, the left-heavy view, the sandwich view for "where is this function called from" — and it has a design decision that makes sharing genuinely hard:
it never uploads your profile. speedscope.app is a static page that parses the file in your browser. Nothing leaves the machine, which is the right call for a production profile, and which means there is no URL to send anybody.
So the normal workflow is: attach profile.json to a message, and add a
paragraph explaining that the recipient should go to speedscope.app and drag
the file in. Most of them don't.
Publish the viewer with the trace
speedscope ships as a static bundle. Publish that bundle as the report and put the trace next to it:
# get the static build
npm pack speedscope && tar -xzf speedscope-*.tgz # → package/dist/release/
# produce a trace in a format speedscope reads
py-spy record -f speedscope -o profile.speedscope.json --pid 12345
Drop the folder into the app:
index.htmlbecomes the report body.- The viewer's JS/CSS and your trace JSON upload alongside it as assets, and their relative references are rewritten to the uploaded copies.
- Scripts run inside a sandboxed iframe (
allow-scripts, noallow-same-origin), so the flame chart, the view switcher and the keyboard navigation all behave.
Append #profileURL= pointing at the uploaded trace, or simply upload a trace
named so the reader can open it from speedscope's file picker — the point is
that the viewer is already on screen when they follow the link.
Which profilers this covers
speedscope reads more formats than most people realize, so this one report shape works across languages:
| Source | Command |
|---|---|
| Python | py-spy record -f speedscope -o out.json --pid <pid> |
| Node.js | node --cpu-prof app.js → *.cpuprofile |
| Chrome / DevTools | Performance panel → Save profile |
| Go | go tool pprof -raw → pprof-to-speedscope |
| Linux perf | perf script > out.perf |
| Ruby | stackprof --format=speedscope |
That is the practical argument for standardizing on it: one viewer, one publish step, whatever the runtime was.
Keep one URL per investigation
PATCH the same report id as you re-profile, and the link you pasted into the incident channel on the first capture is still the right link on the fifth:
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html index.html \
--arg t "Profile — checkout, after cache fix" '{title: $t, html: $html}')"
What review adds
- Anchored threads on the frame under discussion — see commenting on HTML.
- Revisions, so a sequence of captures reads as an investigation.
- Access per report — the trace carries real stack frames from a real process. Private, team or named-reviewer access is the right default here. See the sharing model.
Limits
- Entry HTML: 5 MB. Assets: 25 MB per file, 250 MB and 500 files total. The trace JSON is the file to watch — a long high-frequency capture is the one that gets big; shorten the window rather than downsampling the stacks.
- 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.