# Share a Profiling Report — Send the Flame Graph, Not the Number

Canonical: https://commareports.com/share-profiling-report
Published: 2026-08-24

> pyinstrument and clinic.js write self-contained HTML profiles that never leave your laptop. Publish one to Comma for a URL where the call tree stays explorable and the fix gets discussed on the hot frame.

# Share a profiling report

Profilers produce the most persuasive artifact in performance work and the
least shareable one. `pyinstrument` gives you a call tree with the wall-clock
cost of every frame. `clinic.js` gives you the event-loop picture. A flame
graph shows you, in one image, exactly which stack ate the request.

Then the sharing step flattens all of it into a sentence — _"serialization is
about 40% of it"_ — and the team makes a decision about a claim instead of
about evidence.

## Publish it

```bash
pyinstrument --renderer html -o profile.html -m pytest tests/test_slow.py
```

```python
import os, requests

r = requests.post(
    "https://commareports.com/api/v1/reports",
    headers={"Authorization": f"Bearer {os.environ['COMMA_API_TOKEN']}"},
    json={
        "title": "Checkout endpoint — 1.8s p95",
        "description": "40% in JSON serialization, all of it in the nested cart items.",
        "html": open("profile.html").read(),
    },
)
print(r.json()["url"])
```

One self-contained file, one URL. The call tree stays interactive inside the
sandboxed iframe (`allow-scripts`, no `allow-same-origin`) — collapsing
frames, hiding library code, reading the timing bars.

Other profilers, same motion:

- **clinic.js** — `clinic doctor -- node server.js` writes a self-contained
  HTML report; publish the file.
- **flamegraph.pl** — the output is SVG. Wrap it in a minimal HTML page with
  the paragraph explaining what was under load, and publish that. The
  paragraph is what makes it a report rather than an image.
- **`cProfile` + snakeviz** — snakeviz is a local server, not an artifact.
  Render with pyinstrument or export the stats and publish a rendered view.

## The description does the work

A profile with no context is a wall of frames. The two sentences at the top —
what you were running, what load, what you think it means — are what turn it
into something a reviewer can engage with. Write them into the report
`description` and the first heading; they are what someone reads before
deciding whether to open the tree.

## Then let people disagree

The best outcome of a shared profile is someone who knows the code better
saying so, on the frame:

- **"That's the N+1 — the serializer lazy-loads each cart item."**
- **"This was profiled with the cache cold. Rerun it warm before we act."**
- **"Fixed in #4412. Reprofile and we should see this frame vanish."**

Threads anchor to the rendered content and survive the next profile PATCHed to
the same report id — so the before and after live at one URL with a diff
between them. See [commenting on HTML](/comment-on-html).

## Limits

- **HTML body: 5 MB.** A deep profile of a long run can exceed it — profile a
  narrower slice, or raise the interval so the tree is smaller and, usually,
  more readable.
- **Scripts run, sandboxed:** no same-origin fetches.
- **60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Publish a profile →](https://commareports.com/)**

### Related

- [Share a benchmark report](/share-benchmark-report) · [Share a k6 load-test report](/share-k6-load-test-report)
- [Share a bundle analyzer report](/share-bundle-analyzer-report) · [Share a Lighthouse report](/share-lighthouse-report)
- [Interactive HTML reports](/interactive-html-reports) · [Publish from CI](/docs/ci)
