Flame graph
<scope-flame> displays weighted call trees with navigation and pinned details. The lightweight createFlame factory accepts the same document.
Loading interactive chart…
Inclusive widths computed from supplied self weights.
Hover to read · Click to select
<scope-flame id="chart">
<nav slot="toolbar" class="chart-controls" aria-label="Chart controls"></nav>
</scope-flame>
<script type="module">
import '@scopedb/scopeview/register';
import chartDocument from './chart.js';
const chart = document.querySelector('#chart');
const toolbar = chart.querySelector('[slot="toolbar"]');
const labels = {
"zoom-in": "+",
"zoom-out": "−"
};
function renderToolbar() {
const controls = chart.controls;
for (const button of toolbar.querySelectorAll('[data-action]')) {
if (!controls.some((control) => control.action === button.dataset.action)) button.remove();
}
for (const control of controls) {
let button = toolbar.querySelector('[data-action="' + control.action + '"]');
if (!button) {
button = document.createElement('button');
button.type = 'button';
button.dataset.action = control.action;
button.addEventListener('click', () => chart.performAction(control.action));
toolbar.append(button);
}
button.textContent = labels[control.action] || control.label;
button.setAttribute('aria-label', control.label);
button.disabled = control.disabled;
if (control.expanded !== undefined) button.setAttribute('aria-expanded', String(control.expanded));
else button.removeAttribute('aria-expanded');
}
}
chart.addEventListener('scope-controls-change', renderToolbar);
chart.configure({
dismissOnOutside: false
});
chart.document = chartDocument;
renderToolbar();
</script>
<style>
#chart {
color-scheme: light;
--scope-chart-height: 260px;
--color-text-primary: #202b38;
--color-text-secondary: #657286;
--color-text-tertiary: #7b8798;
--color-surface: #ffffff;
--color-border: #dce3eb;
--color-border-light: #edf0f4;
}
.chart-controls[slot="toolbar"] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
margin-top: 12px;
padding: 8px 0;
border-top: 1px solid var(--color-border-light);
font-size: var(--scope-chart-tooltip-font-size, 12px);
line-height: 1.4;
}
.chart-controls[slot="toolbar"] button {
border: 0;
border-radius: var(--radius-chart-control, 4px);
padding: 5px 8px;
font: inherit;
color: var(--color-text-secondary);
background: transparent;
cursor: pointer;
}
.chart-controls[slot="toolbar"] button:hover:not(:disabled),
.chart-controls[slot="toolbar"] button[aria-expanded="true"] {
color: var(--color-text-primary);
background: var(--color-surface-hover);
}
.chart-controls[slot="toolbar"] button:disabled {
opacity: 0.45;
cursor: default;
}
.chart-controls[slot="toolbar"] button:focus-visible {
outline: 2px solid var(--color-text-brand, #0876dd);
outline-offset: 1px;
}
</style>Copy the example
Follow Run an example for project setup and the development server. Save this example's index.html, chart.js and data.json together. Replace the frames in data.json, and set their unit in chart.js. The mount file includes a replaceable toolbar.
Supply self weights
Each frame needs id, name and a non-negative integer-string selfWeight. Supply self weights, not inclusive totals: ScopeView adds descendants to determine each frame's width. Use parentId to connect the tree, and unit for its weight unit, such as samples or bytes. Optional file and line fields preserve source locations. Repeated names stay distinct when their IDs differ. See profile documents.
Root widths partition the total supplied weight. Sibling order follows the input. Collapsing changes visibility without changing weight; zero-weight frames retain keyboard readings.
Read and navigate
Hover shows self weight, inclusive weight and share of the supplied profile. Click or Enter pins details below the controls. Another frame stays hoverable. Drag pans, Ctrl/Command-wheel zooms, and the toolbar provides zoom/reset actions. Color identifies a frame name, not weight.
To reveal an initial frame, register before assigning its document:
import type { ScopeFlameElement } from '@scopedb/scopeview/element';
export function revealFrame(chart: ScopeFlameElement, id: string) {
chart.addEventListener('scope-ready', () => chart.focusNode(id, { pin: true }), { once: true });
}Inspection events contain the original frames in detail.sources. See component API for disclosure and navigation controls, view state for persistence, and styling for appearance.