{"spec_id":"flamegraph-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// flamegraph-basic: Flame Graph for Performance Profiling\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 82/100 | Created: 2026-06-08\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Simulated CPU profiling tree. Each node carries `self` samples; totals are\n// folded up so a bar's width encodes self + descendant time.\nconst profile = {\n  name: \"main\",\n  self: 0,\n  children: [\n    { name: \"load_config\", self: 14, children: [\n      { name: \"read_yaml\", self: 10 },\n      { name: \"merge_env\", self: 6 },\n    ]},\n    { name: \"init_database\", self: 4, children: [\n      { name: \"create_pool\", self: 6, children: [\n        { name: \"open_socket\", self: 18 },\n        { name: \"auth_handshake\", self: 14 },\n        { name: \"negotiate_tls\", self: 12 },\n      ]},\n      { name: \"run_migrations\", self: 4, children: [\n        { name: \"alter_schema\", self: 22 },\n        { name: \"seed_data\", self: 6, children: [\n          { name: \"insert_users\", self: 18 },\n          { name: \"insert_orders\", self: 24 },\n          { name: \"insert_products\", self: 16 },\n        ]},\n      ]},\n    ]},\n    { name: \"process_requests\", self: 8, children: [\n      { name: \"parse_request\", self: 6, children: [\n        { name: \"decode_json\", self: 20, children: [\n          { name: \"tokenize\", self: 12 },\n          { name: \"build_ast\", self: 14 },\n        ]},\n        { name: \"validate_schema\", self: 16 },\n      ]},\n      { name: \"route_handler\", self: 4, children: [\n        { name: \"query_users\", self: 6, children: [\n          { name: \"sql_execute\", self: 6, children: [\n            { name: \"plan_query\", self: 16 },\n            { name: \"scan_index\", self: 30 },\n            { name: \"fetch_rows\", self: 22 },\n            { name: \"decode_pages\", self: 18 },\n          ]},\n          { name: \"build_query\", self: 14 },\n        ]},\n        { name: \"transform_result\", self: 6, children: [\n          { name: \"map_fields\", self: 16, children: [\n            { name: \"lookup_dict\", self: 12 },\n            { name: \"format_currency\", self: 8 },\n          ]},\n          { name: \"filter_rows\", self: 12 },\n          { name: \"sort_results\", self: 14 },\n        ]},\n        { name: \"serialize_response\", self: 6, children: [\n          { name: \"encode_json\", self: 18, children: [\n            { name: \"escape_strings\", self: 10 },\n            { name: \"write_buffer\", self: 12 },\n          ]},\n        ]},\n      ]},\n      { name: \"log_request\", self: 4, children: [\n        { name: \"format_line\", self: 8 },\n        { name: \"write_log\", self: 10 },\n      ]},\n    ]},\n    { name: \"render_template\", self: 4, children: [\n      { name: \"compile_template\", self: 14, children: [\n        { name: \"lex_tokens\", self: 8 },\n      ]},\n      { name: \"interpolate_values\", self: 4, children: [\n        { name: \"escape_html\", self: 16 },\n        { name: \"resolve_partials\", self: 12 },\n      ]},\n    ]},\n    { name: \"send_metrics\", self: 8, children: [\n      { name: \"collect_counters\", self: 8 },\n      { name: \"flush_buffer\", self: 4, children: [\n        { name: \"http_post\", self: 16 },\n        { name: \"retry_backoff\", self: 6 },\n      ]},\n    ]},\n    { name: \"network_io\", self: 4, children: [\n      { name: \"read_socket\", self: 12 },\n      { name: \"write_socket\", self: 10 },\n      { name: \"dns_lookup\", self: 8 },\n    ]},\n  ],\n};\n\n// Fold descendant samples into each node's total.\nfunction computeTotals(node) {\n  let total = node.self || 0;\n  for (const child of node.children || []) total += computeTotals(child);\n  node.total = total;\n  return total;\n}\ncomputeTotals(profile);\n\n// Lay out rectangles bottom-up: depth = row, [x0, x1] proportional to total.\nconst rectangles = [];\nfunction layout(node, x0, depth) {\n  rectangles.push({ name: node.name, x0, x1: x0 + node.total, depth });\n  const children = (node.children || []).slice().sort((a, b) => a.name.localeCompare(b.name));\n  let cx = x0;\n  for (const child of children) {\n    layout(child, cx, depth + 1);\n    cx += child.total;\n  }\n}\nlayout(profile, 0, 0);\n\nconst totalSamples = profile.total;\nconst maxDepth = Math.max.apply(null, rectangles.map((r) => r.depth));\n\n// Warm Imprint subset for flame graph aesthetic — ochre, matte red, amber —\n// keyed deterministically off the function name so repeat frames keep their\n// identity. Brand green (#009E73, Imprint position 1) anchors the root frame,\n// satisfying the \"first categorical series\" rule while preserving the\n// conventional warm flame palette for stacked frames above.\nconst warmColors = [\"#BD8233\", \"#AE3030\", \"#DDCC77\"];\nfunction colorFor(rect) {\n  if (rect.depth === 0) return t.palette[0];\n  let h = 0;\n  for (let i = 0; i < rect.name.length; i++) h = (h * 31 + rect.name.charCodeAt(i)) >>> 0;\n  return warmColors[h % warmColors.length];\n}\n\n// WCAG relative luminance — pick label ink from per-bar fill luminance so\n// labels keep contrast regardless of theme (dark text on light fills, light\n// text on dark fills).\nfunction labelInkFor(hex) {\n  const h = hex.replace(\"#\", \"\");\n  const channels = [0, 2, 4].map((i) => {\n    const c = parseInt(h.slice(i, i + 2), 16) / 255;\n    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n  });\n  const lum = 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];\n  return lum > 0.5 ? \"#1A1A17\" : \"#FAF8F1\";\n}\n\n// Encode each rectangle as [x0, x1, depth, name]. Pre-compute fill + label ink.\nconst data = rectangles.map((r) => {\n  const fill = colorFor(r);\n  return {\n    value: [r.x0, r.x1, r.depth, r.name, fill, labelInkFor(fill)],\n    itemStyle: { color: fill },\n  };\n});\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"flamegraph-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 18,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: \"normal\" },\n  },\n  grid: { left: 90, right: 50, top: 90, bottom: 80 },\n  xAxis: {\n    type: \"value\",\n    min: 0,\n    max: totalSamples,\n    name: \"Samples (proportional CPU time)\",\n    nameLocation: \"middle\",\n    nameGap: 40,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    min: 0,\n    max: maxDepth + 1,\n    interval: 1,\n    name: \"Call stack depth\",\n    nameLocation: \"middle\",\n    nameGap: 50,\n    nameTextStyle: { color: t.ink, fontSize: 16 },\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: (v) => (Number.isInteger(v) ? v : \"\") },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  series: [{\n    type: \"custom\",\n    encode: { x: [0, 1], y: 2 },\n    renderItem: (params, api) => {\n      const x0 = api.coord([api.value(0), api.value(2)])[0];\n      const x1 = api.coord([api.value(1), api.value(2)])[0];\n      const yTop = api.coord([0, api.value(2) + 1])[1];\n      const yBottom = api.coord([0, api.value(2)])[1];\n      const width = Math.max(0, x1 - x0);\n      const height = Math.max(0, yBottom - yTop);\n      const name = api.value(3);\n      const fill = api.value(4);\n      const labelInk = api.value(5);\n      // Only label when the function name + left inset + right safety gap\n      // genuinely fits — keeps adjacent in-bar labels from butting at depth 3.\n      const estTextWidth = String(name).length * 7.2;\n      const showLabel = width > estTextWidth + 28;\n      return {\n        type: \"rect\",\n        shape: { x: x0, y: yTop, width: Math.max(0, width - 2), height: Math.max(0, height - 2) },\n        style: { fill, stroke: t.pageBg, lineWidth: 1 },\n        textContent: showLabel ? {\n          type: \"text\",\n          style: {\n            text: name,\n            fill: labelInk,\n            font: '13px -apple-system, \"Segoe UI\", Roboto, sans-serif',\n            textAlign: \"left\",\n            textVerticalAlign: \"middle\",\n          },\n        } : undefined,\n        textConfig: showLabel ? { position: \"insideLeft\", inside: true, distance: 12 } : undefined,\n      };\n    },\n    data,\n  }],\n});\n\nwindow.__anyplotReady = true;\n"}