> ## Documentation Index
> Fetch the complete documentation index at: https://numpyts.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# numpy-ts vs. NumPy (Pyodide)

export const BenchmarkReport = ({data, detailUrl}) => {
  const summary = data?.summary || ({});
  const meta = data?.meta || ({});
  const categories = Array.isArray(data?.categories) ? data.categories : [];
  const dtypeStats = Array.isArray(data?.dtypeStats) ? data.dtypeStats : [];
  const THEME_COLORS = {
    light: {
      cardBg: '#ffffff',
      mutedCardBg: '#fafafa',
      border: '#e5e7eb',
      text: '#111827',
      mutedText: '#6b7280',
      chartTrack: '#f3f4f6',
      tableHeadBg: '#f9fafb',
      tableHeadText: '#374151',
      tableRowBorder: '#e5e7eb',
      tableRowAlt: '#fafafa',
      ratioGoodText: '#1f9d55',
      ratioGoodBg: '#dcfce7',
      ratioOkText: '#b7791f',
      ratioOkBg: '#fef3c7',
      ratioBadText: '#c53030',
      ratioBadBg: '#fee2e2',
      chartGood: '#22c55e',
      chartOk: '#f59e0b',
      chartBad: '#ef4444'
    },
    dark: {
      cardBg: '#1e1e1e',
      mutedCardBg: '#1a1a1a',
      border: '#333333',
      text: '#d4d4d4',
      mutedText: '#888888',
      chartTrack: '#2a2a2a',
      tableHeadBg: '#161616',
      tableHeadText: '#d4d4d4',
      tableRowBorder: '#333333',
      tableRowAlt: '#161616',
      ratioGoodText: '#86efac',
      ratioGoodBg: '#14532d',
      ratioOkText: '#fde68a',
      ratioOkBg: '#78350f',
      ratioBadText: '#fca5a5',
      ratioBadBg: '#7f1d1d',
      chartGood: '#22c55e',
      chartOk: '#f59e0b',
      chartBad: '#ef4444'
    }
  };
  const DTYPE_COLORS = {
    float64: {
      bg: '#3b6cc9',
      text: '#ffffff'
    },
    float32: {
      bg: '#4a80d8',
      text: '#ffffff'
    },
    float16: {
      bg: '#6196e2',
      text: '#ffffff'
    },
    complex128: {
      bg: '#8b5cc6',
      text: '#ffffff'
    },
    complex64: {
      bg: '#a478d4',
      text: '#ffffff'
    },
    int64: {
      bg: '#2a8a82',
      text: '#ffffff'
    },
    int32: {
      bg: '#3ba69d',
      text: '#ffffff'
    },
    int16: {
      bg: '#55bfb7',
      text: '#1a4a46'
    },
    int8: {
      bg: '#7ad4cd',
      text: '#1a4a46'
    },
    uint64: {
      bg: '#c98a2e',
      text: '#ffffff'
    },
    uint32: {
      bg: '#d9a044',
      text: '#3a2400'
    },
    uint16: {
      bg: '#e4b85e',
      text: '#3a2400'
    },
    uint8: {
      bg: '#eecb7c',
      text: '#3a2400'
    },
    bool: {
      bg: '#cc4466',
      text: '#ffffff'
    }
  };
  const DTYPE_RE = /\s+(float64|float32|float16|complex128|complex64|int64|int32|int16|int8|uint64|uint32|uint16|uint8|bool)$/;
  const [isDarkMode, setIsDarkMode] = useState(() => document.documentElement.classList.contains('dark'));
  const [openCategories, setOpenCategories] = useState({});
  const [isMetaOpen, setIsMetaOpen] = useState(false);
  const [isDtypeOpen, setIsDtypeOpen] = useState(false);
  const [isMobile, setIsMobile] = useState(() => window.innerWidth < 640);
  const [isNarrow, setIsNarrow] = useState(() => window.innerWidth < 900);
  const [detailMap, setDetailMap] = useState(null);
  const [detailLoading, setDetailLoading] = useState(false);
  const detailFetched = useRef(false);
  const fetchDetail = () => {
    if (detailFetched.current || !detailUrl) return;
    detailFetched.current = true;
    setDetailLoading(true);
    const absUrl = new URL(detailUrl, window.location.href).href;
    const buildMap = d => {
      const cats = d && d.categories || [];
      const m = {};
      for (const c of cats) m[c.name] = c.benchmarks || [];
      return m;
    };
    const fallback = () => {
      fetch(absUrl).then(r => r.json()).then(d => setDetailMap(buildMap(d))).catch(() => {
        detailFetched.current = false;
      }).finally(() => setDetailLoading(false));
    };
    try {
      if (typeof Worker === 'undefined' || typeof Blob === 'undefined') return fallback();
      const code = "self.onmessage=async e=>{try{const{url,key}=e.data;const r=await fetch(url);const d=await r.json();const cats=(d&&d[key])||[];const m={};for(const c of cats)m[c.name]=c.benchmarks||[];self.postMessage({ok:true,map:m});}catch(err){self.postMessage({ok:false});}}";
      const blobUrl = URL.createObjectURL(new Blob([code], {
        type: 'application/javascript'
      }));
      const worker = new Worker(blobUrl);
      const cleanup = () => {
        worker.terminate();
        URL.revokeObjectURL(blobUrl);
      };
      worker.onmessage = e => {
        if (e.data && e.data.ok) setDetailMap(e.data.map); else detailFetched.current = false;
        setDetailLoading(false);
        cleanup();
      };
      worker.onerror = () => {
        detailFetched.current = false;
        setDetailLoading(false);
        cleanup();
      };
      worker.postMessage({
        url: absUrl,
        key: 'categories'
      });
    } catch {
      fallback();
    }
  };
  useEffect(() => {
    if (!detailUrl) return;
    let ricId, timeoutId;
    const schedule = () => {
      const ric = window.requestIdleCallback;
      if (ric) ricId = ric(() => fetchDetail(), {
        timeout: 2000
      }); else timeoutId = setTimeout(fetchDetail, 200);
    };
    if (document.readyState === 'complete') schedule(); else window.addEventListener('load', schedule, {
      once: true
    });
    return () => {
      window.removeEventListener('load', schedule);
      if (ricId && window.cancelIdleCallback) window.cancelIdleCallback(ricId);
      if (timeoutId) clearTimeout(timeoutId);
    };
  }, []);
  const toggleCategory = name => {
    fetchDetail();
    setOpenCategories(prev => ({
      ...prev,
      [name]: !prev[name]
    }));
  };
  useEffect(() => {
    const observer = new MutationObserver(() => {
      setIsDarkMode(document.documentElement.classList.contains('dark'));
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    const handler = () => {
      setIsMobile(window.innerWidth < 640);
      setIsNarrow(window.innerWidth < 900);
    };
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  const colors = THEME_COLORS[isDarkMode ? 'dark' : 'light'];
  const HoverBar = useMemo(() => function HoverBar({tip, width, color, rounded, opacity, isDarkMode}) {
    const [show, setShow] = useState(false);
    const [pos, setPos] = useState(null);
    const ref = useRef(null);
    const lastPointerType = useRef('mouse');
    useEffect(() => {
      if (!show) {
        setPos(null);
        return;
      }
      const updatePos = () => {
        if (!ref.current) return;
        const r = ref.current.getBoundingClientRect();
        setPos({
          top: r.top,
          left: r.left + r.width / 2
        });
      };
      updatePos();
      const onDocPointerDown = e => {
        if (ref.current && !ref.current.contains(e.target)) setShow(false);
      };
      document.addEventListener('pointerdown', onDocPointerDown);
      window.addEventListener('scroll', updatePos, true);
      window.addEventListener('resize', updatePos);
      return () => {
        document.removeEventListener('pointerdown', onDocPointerDown);
        window.removeEventListener('scroll', updatePos, true);
        window.removeEventListener('resize', updatePos);
      };
    }, [show]);
    return <div ref={ref} style={{
      height: '100%',
      width,
      position: 'relative',
      cursor: 'default'
    }} onPointerEnter={e => {
      if (e.pointerType === 'mouse') setShow(true);
    }} onPointerLeave={e => {
      if (e.pointerType === 'mouse') setShow(false);
    }} onPointerDown={e => {
      lastPointerType.current = e.pointerType;
    }} onClick={() => {
      if (lastPointerType.current !== 'mouse') setShow(s => !s);
    }}>
        <div style={{
      height: '100%',
      width: '100%',
      background: color,
      borderRadius: rounded ? 7 : 0,
      opacity: opacity ?? 1,
      filter: show ? 'brightness(1.3)' : 'none',
      transition: 'filter 0.15s'
    }} />
        {tip && show && pos && <div style={{
      position: 'fixed',
      top: pos.top - 6,
      left: pos.left,
      transform: 'translate(-50%, -100%)',
      padding: '4px 8px',
      borderRadius: 6,
      background: isDarkMode ? '#2a2a2a' : '#111',
      color: '#fff',
      fontSize: 11,
      whiteSpace: 'nowrap',
      zIndex: 2147483647,
      pointerEvents: 'none',
      boxShadow: '0 2px 8px rgba(0,0,0,0.4)'
    }}>
            {tip}
          </div>}
      </div>;
  }, []);
  const BenchmarkName = ({name}) => {
    const s = String(name);
    const m = s.match(DTYPE_RE);
    const dtype = m ? m[1] : 'float64';
    const base = m ? s.slice(0, -m[0].length) : s;
    const dc = DTYPE_COLORS[dtype] || DTYPE_COLORS.float64;
    return <>
        {base}{' '}
        <span style={{
      display: 'inline-block',
      fontSize: '0.72em',
      fontWeight: 600,
      padding: '2px 8px',
      borderRadius: 999,
      background: dc.bg,
      color: dc.text,
      verticalAlign: 'middle',
      whiteSpace: 'nowrap'
    }}>
          {dtype}
        </span>
      </>;
  };
  const formatOps = ops => {
    if (!Number.isFinite(ops) || ops <= 0) return '-';
    if (ops >= 1_000_000_000) return `${(ops / 1_000_000_000).toFixed(2)}B/s`;
    if (ops >= 1_000_000) return `${(ops / 1_000_000).toFixed(2)}M/s`;
    if (ops >= 1_000) return `${(ops / 1_000).toFixed(2)}K/s`;
    return `${ops.toFixed(1)}/s`;
  };
  const formatRatio = ratio => {
    if (!Number.isFinite(ratio)) return '-';
    return `${ratio.toFixed(2)}x`;
  };
  const ratioTip = ratio => {
    if (!Number.isFinite(ratio)) return '';
    return ratio >= 1 ? `${ratio.toFixed(2)}x faster than NumPy` : `${ratio.toFixed(2)}x slower than NumPy`;
  };
  const RatioDisplay = ({ratio, size}) => {
    if (!Number.isFinite(ratio)) return <span>-</span>;
    const numSize = size === 'lg' ? 28 : 20;
    const suffixSize = size === 'lg' ? 13 : 11;
    return <span>
        <span style={{
      fontSize: numSize,
      fontWeight: 700,
      color: colors.text
    }}>{ratio.toFixed(2)}x</span>
        <span style={{
      fontSize: suffixSize,
      fontWeight: 400,
      color: colors.mutedText,
      marginLeft: 4
    }}>{ratio >= 1 ? 'faster than NumPy' : 'slower than NumPy'}</span>
      </span>;
  };
  const ratioColor = ratio => {
    if (ratio >= 1) return colors.ratioGoodText;
    if (ratio >= 0.75) return colors.ratioOkText;
    return colors.ratioBadText;
  };
  const ratioBg = ratio => {
    if (ratio >= 1) return colors.ratioGoodBg;
    if (ratio >= 0.75) return colors.ratioOkBg;
    return colors.ratioBadBg;
  };
  const chartColor = ratio => {
    if (ratio >= 1) return colors.chartGood;
    if (ratio >= 0.75) return colors.chartOk;
    return colors.chartBad;
  };
  const SummaryCard = ({label, value, style}) => <div style={{
    border: `1px solid ${colors.border}`,
    borderRadius: 10,
    padding: 14,
    background: colors.cardBg,
    ...style
  }}>
      <div style={{
    fontSize: 12,
    color: colors.mutedText,
    marginBottom: 6
  }}>{label}</div>
      <div style={{
    fontSize: 28,
    fontWeight: 700,
    color: colors.text
  }}>{value}</div>
    </div>;
  const BASELINE_PCT = 20;
  const barScale = (value, max) => {
    if (value <= 0) return 2;
    if (value <= 1) return Math.max(2, BASELINE_PCT * value);
    if (max <= 1) return BASELINE_PCT;
    return Math.min(100, BASELINE_PCT + Math.log(value) / Math.log(max) * (100 - BASELINE_PCT));
  };
  const BarTrack = ({children}) => <div style={{
    background: colors.chartTrack,
    height: 14,
    borderRadius: 7,
    position: 'relative'
  }}>
      <div style={{
    position: 'absolute',
    left: `${BASELINE_PCT}%`,
    top: 0,
    bottom: 0,
    width: 1,
    borderLeft: `1px dashed ${colors.mutedText}`,
    opacity: 0.4,
    zIndex: 1
  }} />
      {children}
    </div>;
  const maxCategorySpeedup = Math.max(...categories.map(c => c.avgSpeedup || 0), 1);
  const maxDtypeSpeedup = Math.max(...dtypeStats.map(d => d.avgSpeedup || 0), 1);
  const barGridCols = isMobile ? '90px 1fr 44px' : '140px 1fr 52px';
  return <div style={{
    color: colors.text
  }}>
      <div style={{
    display: 'grid',
    gap: 12,
    gridTemplateColumns: isMobile ? '1fr 1fr' : isNarrow ? '1fr' : '2fr 1fr 1fr',
    marginBottom: 20
  }}>
        <SummaryCard label="Average" value={<RatioDisplay ratio={summary.avgSpeedup} size="lg" />} style={isMobile ? {
    gridColumn: '1 / -1'
  } : undefined} />
        <SummaryCard label="Best case" value={formatRatio(summary.bestCase)} />
        <SummaryCard label="Worst case" value={formatRatio(summary.worstCase)} />
      </div>

      <div style={{
    border: `1px solid ${colors.border}`,
    borderRadius: 10,
    padding: 16,
    background: colors.cardBg,
    marginBottom: 20
  }}>
        <div style={{
    fontSize: 16,
    fontWeight: 600,
    marginBottom: 12,
    color: colors.text
  }}>Average Performance vs. NumPy by Category</div>
        <div style={{
    display: 'grid',
    gap: 10
  }}>
          {categories.map((category, catIdx) => {
    const ratio = Number(category.avgSpeedup) || 0;
    const widthPct = barScale(ratio, maxCategorySpeedup);
    return <div key={String(category.name)} style={{
      display: 'grid',
      gridTemplateColumns: barGridCols,
      gap: 10,
      alignItems: 'center'
    }}>
                <div style={{
      fontFamily: 'monospace',
      fontSize: 12,
      color: colors.text,
      overflow: 'hidden',
      textOverflow: 'ellipsis',
      whiteSpace: 'nowrap'
    }}>
                  <a href={`#cat-${String(category.name).replace(/\s+/g, '-').toLowerCase()}`} onClick={e => {
      e.preventDefault();
      fetchDetail();
      const catName = String(category.name);
      setOpenCategories(prev => ({
        ...prev,
        [catName]: true
      }));
      setTimeout(() => {
        const el = document.getElementById(`cat-${catName.replace(/\s+/g, '-').toLowerCase()}`);
        if (el) el.scrollIntoView({
          behavior: 'smooth',
          block: 'start'
        });
      }, 50);
    }} style={{
      color: 'inherit',
      textDecoration: 'none',
      cursor: 'pointer'
    }} onMouseEnter={e => e.currentTarget.style.textDecoration = 'underline'} onMouseLeave={e => e.currentTarget.style.textDecoration = 'none'}>{String(category.name)}</a>
                </div>
                <BarTrack>
                  <HoverBar tip={ratioTip(ratio)} width={`${widthPct}%`} color={chartColor(ratio)} rounded isDarkMode={isDarkMode} />
                </BarTrack>
                <div style={{
      textAlign: 'right',
      fontWeight: 600,
      fontSize: 12,
      color: colors.text
    }}>{formatRatio(ratio)}</div>
              </div>;
  })}
        </div>
      </div>

      {dtypeStats.length > 0 && <div style={{
    border: `1px solid ${colors.border}`,
    borderRadius: 10,
    background: colors.cardBg,
    marginBottom: 20,
    overflow: 'hidden'
  }}>
          <button onClick={() => setIsDtypeOpen(v => !v)} style={{
    display: 'flex',
    alignItems: 'center',
    gap: 6,
    width: '100%',
    padding: 14,
    background: 'transparent',
    border: 'none',
    cursor: 'pointer',
    fontWeight: 600,
    fontSize: 14,
    color: colors.text,
    textAlign: 'left'
  }}>
            <span style={{
    fontSize: 10,
    display: 'inline-block',
    transform: isDtypeOpen ? 'rotate(90deg)' : 'rotate(0deg)',
    transition: 'transform 0.15s'
  }}>▶</span>
            Average Performance by DType
          </button>
          <div style={{
    maxHeight: isDtypeOpen ? 2000 : 0,
    overflow: isDtypeOpen ? 'visible' : 'hidden',
    transition: isDtypeOpen ? 'max-height 0.4s ease-in' : 'max-height 0.3s ease-out'
  }}>
            <div style={{
    padding: '0 14px 14px',
    display: 'grid',
    gap: 10
  }}>
              {dtypeStats.map(({dtype, count, avgSpeedup}) => {
    const widthPct = barScale(avgSpeedup, maxDtypeSpeedup);
    const dc = DTYPE_COLORS[dtype] || DTYPE_COLORS.float64;
    return <div key={dtype} style={{
      display: 'grid',
      gridTemplateColumns: barGridCols,
      gap: 10,
      alignItems: 'center'
    }}>
                    <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: 6,
      minWidth: 0
    }}>
                      <span style={{
      display: 'inline-block',
      fontSize: '0.72em',
      fontWeight: 600,
      padding: '2px 8px',
      borderRadius: 999,
      background: dc.bg,
      color: dc.text,
      whiteSpace: 'nowrap',
      flexShrink: 0
    }}>
                        {dtype}
                      </span>
                      {!isMobile && <span style={{
      fontSize: 11,
      color: colors.mutedText,
      whiteSpace: 'nowrap'
    }}>({count})</span>}
                    </div>
                    <BarTrack>
                      <HoverBar tip={ratioTip(avgSpeedup)} width={`${widthPct}%`} color={chartColor(avgSpeedup)} rounded isDarkMode={isDarkMode} />
                    </BarTrack>
                    <div style={{
      textAlign: 'right',
      fontWeight: 600,
      fontSize: 12,
      color: colors.text
    }}>{formatRatio(avgSpeedup)}</div>
                  </div>;
  })}
            </div>
          </div>
        </div>}

      <div style={{
    border: `1px solid ${colors.border}`,
    borderRadius: 10,
    background: colors.mutedCardBg,
    marginBottom: 20,
    overflow: 'hidden'
  }}>
        <button onClick={() => setIsMetaOpen(v => !v)} style={{
    display: 'flex',
    alignItems: 'center',
    gap: 6,
    width: '100%',
    padding: 14,
    background: 'transparent',
    border: 'none',
    cursor: 'pointer',
    fontWeight: 600,
    fontSize: 14,
    color: colors.text,
    textAlign: 'left'
  }}>
          <span style={{
    fontSize: 10,
    display: 'inline-block',
    transform: isMetaOpen ? 'rotate(90deg)' : 'rotate(0deg)',
    transition: 'transform 0.15s'
  }}>▶</span>
          Benchmark Details
        </button>
        <div style={{
    maxHeight: isMetaOpen ? 500 : 0,
    overflow: 'hidden',
    transition: isMetaOpen ? 'max-height 0.3s ease-in' : 'max-height 0.3s ease-out'
  }}>
          <div style={{
    padding: '0 14px 14px'
  }}>
            <div><strong>Generated:</strong> {meta.generatedAt || '-'}</div>
            <div><strong>Source:</strong> <code>{meta.sourceJson || '-'}</code></div>
            {meta.runtimes && Object.entries(meta.runtimes).map(([rt, ver]) => <div key={rt}><strong>{rt.charAt(0).toUpperCase() + rt.slice(1)}:</strong> <code>{ver}</code></div>)}
            {meta.pythonVersion ? <div><strong>Python:</strong> <code>{meta.pythonVersion}</code></div> : null}
            {meta.numpyVersion ? <div><strong>NumPy:</strong> <code>{meta.numpyVersion}</code></div> : null}
            <div><strong>numpy-ts:</strong> <code>{meta.numpyTsVersion || '-'}</code></div>
            {meta.machine ? <div><strong>Machine:</strong> <code>{meta.machine}</code></div> : null}
          </div>
        </div>
      </div>

      <h2>Detailed Results</h2>
      <p style={{
    color: colors.mutedText
  }}>
        Higher is better. A speedup over <code>1.00x</code> means <code>numpy-ts</code> was faster than NumPy for that benchmark.
      </p>

      {categories.map(category => {
    const isOpen = !!openCategories[String(category.name)];
    const benchmarks = isOpen ? Array.isArray(category.benchmarks) ? category.benchmarks : detailMap?.[category.name] || [] : null;
    return <div key={String(category.name)} id={`cat-${String(category.name).replace(/\s+/g, '-').toLowerCase()}`} style={{
      marginTop: 18,
      border: `1px solid ${colors.border}`,
      borderRadius: 10,
      background: colors.cardBg,
      overflow: 'hidden',
      scrollMarginTop: 80
    }}>
            <button onClick={() => toggleCategory(String(category.name))} style={{
      display: 'flex',
      alignItems: 'center',
      gap: 6,
      width: '100%',
      padding: '12px 14px',
      background: 'transparent',
      border: 'none',
      cursor: 'pointer',
      fontWeight: 600,
      fontSize: 'inherit',
      color: colors.text,
      textAlign: 'left'
    }}>
              <span style={{
      fontSize: 10,
      display: 'inline-block',
      transform: isOpen ? 'rotate(90deg)' : 'rotate(0deg)',
      transition: 'transform 0.15s'
    }}>▶</span>
              {String(category.name)} ({category.count} benchmarks, speedup {(Number(category.avgSpeedup) || 0).toFixed(2)}x)
            </button>
            {isOpen && <div style={{
      padding: '0 14px 14px'
    }}>
                <div style={{
      fontSize: 13,
      color: colors.mutedText,
      marginBottom: 8
    }}>
                  Slower than NumPy: {category.slowerCount} | Faster than NumPy: {category.fasterCount}
                </div>
                <div style={{
      overflowX: 'auto'
    }}>
                  {benchmarks.length === 0 && detailLoading ? <div style={{
      padding: '20px 10px',
      color: colors.mutedText,
      fontSize: 13
    }}>Loading benchmarks…</div> : <table style={{
      minWidth: '100%',
      borderCollapse: 'collapse',
      margin: 0
    }}>
                    <thead>
                      <tr>
                        <th style={{
      textAlign: 'left',
      padding: '8px 10px',
      background: colors.tableHeadBg,
      color: colors.tableHeadText,
      borderBottom: `1px solid ${colors.tableRowBorder}`
    }}>Benchmark</th>
                        <th style={{
      textAlign: 'left',
      padding: '8px 10px',
      background: colors.tableHeadBg,
      color: colors.tableHeadText,
      borderBottom: `1px solid ${colors.tableRowBorder}`
    }}>Speedup</th>
                        <th style={{
      textAlign: 'left',
      padding: '8px 10px',
      background: colors.tableHeadBg,
      color: colors.tableHeadText,
      borderBottom: `1px solid ${colors.tableRowBorder}`
    }}>NumPy ops/s</th>
                        <th style={{
      textAlign: 'left',
      padding: '8px 10px',
      background: colors.tableHeadBg,
      color: colors.tableHeadText,
      borderBottom: `1px solid ${colors.tableRowBorder}`
    }}>numpy-ts ops/s</th>
                      </tr>
                    </thead>
                    <tbody>
                      {benchmarks.map((b, idx) => <tr key={`${String(b.name)}-${idx}`} style={{
      background: idx % 2 === 0 ? 'transparent' : colors.tableRowAlt
    }}>
                          <td style={{
      fontFamily: 'monospace',
      fontSize: 12,
      padding: '8px 10px',
      borderBottom: `1px solid ${colors.tableRowBorder}`,
      color: colors.text
    }}><BenchmarkName name={b.name} /></td>
                          <td style={{
      padding: '8px 10px',
      borderBottom: `1px solid ${colors.tableRowBorder}`
    }}>
                            <span style={{
      background: ratioBg(b.ratio),
      color: ratioColor(b.ratio),
      padding: '2px 8px',
      borderRadius: 999,
      fontWeight: 700,
      fontSize: 12
    }}>
                              {formatRatio(b.ratio)}
                            </span>
                          </td>
                          <td style={{
      padding: '8px 10px',
      borderBottom: `1px solid ${colors.tableRowBorder}`,
      color: colors.text
    }}>{formatOps(b.numpyOps)}</td>
                          <td style={{
      padding: '8px 10px',
      borderBottom: `1px solid ${colors.tableRowBorder}`,
      color: colors.text
    }}>{formatOps(b.numpyTsOps)}</td>
                        </tr>)}
                    </tbody>
                  </table>}
                </div>
              </div>}
          </div>;
  })}
    </div>;
};

export const benchmarkData = {
  "summary": {
    "avgSpeedup": 2.0697,
    "medianSpeedup": 1.9083,
    "bestCase": 63.0266,
    "worstCase": 0.0796,
    "totalBenchmarks": 2390
  },
  "meta": {
    "generatedAt": "2026-05-15 03:06:06 UTC",
    "sourceJson": "benchmarks/results/latest-full-pyodide.json",
    "runtimes": {
      "node": "24.12.0"
    },
    "pythonVersion": "3.13.2",
    "numpyVersion": "2.2.5 (Pyodide/WASM)",
    "numpyTsVersion": "1.4.0",
    "machine": "Apple M4 Max (16 cores, 128 GB, arm64)"
  },
  "categories": [{
    "name": "creation",
    "avgSpeedup": 2.4211,
    "count": 213,
    "fasterCount": 189,
    "slowerCount": 24
  }, {
    "name": "arithmetic",
    "avgSpeedup": 2.6482,
    "count": 295,
    "fasterCount": 282,
    "slowerCount": 13
  }, {
    "name": "math",
    "avgSpeedup": 0.9054,
    "count": 125,
    "fasterCount": 50,
    "slowerCount": 75
  }, {
    "name": "trig",
    "avgSpeedup": 1.1035,
    "count": 216,
    "fasterCount": 119,
    "slowerCount": 97
  }, {
    "name": "gradient",
    "avgSpeedup": 7.5421,
    "count": 22,
    "fasterCount": 22,
    "slowerCount": 0
  }, {
    "name": "linalg",
    "avgSpeedup": 3.1858,
    "count": 269,
    "fasterCount": 230,
    "slowerCount": 39
  }, {
    "name": "reductions",
    "avgSpeedup": 1.7726,
    "count": 413,
    "fasterCount": 328,
    "slowerCount": 85
  }, {
    "name": "manipulation",
    "avgSpeedup": 2.2477,
    "count": 231,
    "fasterCount": 183,
    "slowerCount": 48
  }, {
    "name": "io",
    "avgSpeedup": 2.9571,
    "count": 66,
    "fasterCount": 51,
    "slowerCount": 15
  }, {
    "name": "indexing",
    "avgSpeedup": 1.2853,
    "count": 115,
    "fasterCount": 52,
    "slowerCount": 63
  }, {
    "name": "bitwise",
    "avgSpeedup": 2.015,
    "count": 10,
    "fasterCount": 10,
    "slowerCount": 0
  }, {
    "name": "sorting",
    "avgSpeedup": 1.3177,
    "count": 75,
    "fasterCount": 45,
    "slowerCount": 30
  }, {
    "name": "logic",
    "avgSpeedup": 4.1833,
    "count": 142,
    "fasterCount": 130,
    "slowerCount": 12
  }, {
    "name": "statistics",
    "avgSpeedup": 3.9961,
    "count": 26,
    "fasterCount": 23,
    "slowerCount": 3
  }, {
    "name": "sets",
    "avgSpeedup": 2.4558,
    "count": 33,
    "fasterCount": 24,
    "slowerCount": 9
  }, {
    "name": "random",
    "avgSpeedup": 1.6688,
    "count": 46,
    "fasterCount": 41,
    "slowerCount": 5
  }, {
    "name": "polynomials",
    "avgSpeedup": 4.3598,
    "count": 27,
    "fasterCount": 21,
    "slowerCount": 6
  }, {
    "name": "fft",
    "avgSpeedup": 1.2361,
    "count": 66,
    "fasterCount": 30,
    "slowerCount": 36
  }],
  "dtypeStats": [{
    "dtype": "float64",
    "count": 288,
    "avgSpeedup": 1.8957,
    "medianSpeedup": 1.726
  }, {
    "dtype": "float32",
    "count": 238,
    "avgSpeedup": 2.2101,
    "medianSpeedup": 2.023
  }, {
    "dtype": "float16",
    "count": 211,
    "avgSpeedup": 2.2501,
    "medianSpeedup": 2.283
  }, {
    "dtype": "int64",
    "count": 196,
    "avgSpeedup": 1.5422,
    "medianSpeedup": 1.3612
  }, {
    "dtype": "uint64",
    "count": 188,
    "avgSpeedup": 1.4753,
    "medianSpeedup": 1.354
  }, {
    "dtype": "int32",
    "count": 204,
    "avgSpeedup": 2.0016,
    "medianSpeedup": 2.0499
  }, {
    "dtype": "uint32",
    "count": 189,
    "avgSpeedup": 2.0311,
    "medianSpeedup": 1.9871
  }, {
    "dtype": "int16",
    "count": 185,
    "avgSpeedup": 2.3237,
    "medianSpeedup": 2.6904
  }, {
    "dtype": "uint16",
    "count": 184,
    "avgSpeedup": 2.2992,
    "medianSpeedup": 2.7237
  }, {
    "dtype": "int8",
    "count": 185,
    "avgSpeedup": 2.741,
    "medianSpeedup": 2.8824
  }, {
    "dtype": "uint8",
    "count": 186,
    "avgSpeedup": 2.6794,
    "medianSpeedup": 2.7931
  }, {
    "dtype": "complex128",
    "count": 68,
    "avgSpeedup": 1.8817,
    "medianSpeedup": 1.6762
  }, {
    "dtype": "complex64",
    "count": 68,
    "avgSpeedup": 1.7712,
    "medianSpeedup": 1.5644
  }]
};

Benchmark snapshot comparing numpy-ts against [Pyodide](https://github.com/pyodide/pyodide) NumPy (WASM-compiled CPython + NumPy).

<Tip>
  All benchmarks measure computation time from JS and Python, respectively. To learn more, check out [benchmark methodology](./methodology).
</Tip>

<BenchmarkReport data={benchmarkData} detailUrl="/assets/v1.4.x/vs-pyodide-benchmarks.json" />

<div style={{display: 'none'}} data-llm-summary="true">
  ## Benchmark Summary

  * **Average speedup**: 2.07x vs NumPy
  * **Best case**: 63.03x
  * **Worst case**: 0.08x
  * **Total benchmarks**: 2390
  * **Machine**: Apple M4 Max (16 cores, 128 GB, arm64)
  * **numpy-ts version**: 1.4.0

  ### Performance by Category

  | Category     | Avg Speedup | Count | Faster | Slower |
  | ------------ | ----------- | ----- | ------ | ------ |
  | creation     | 2.42x       | 213   | 189    | 24     |
  | arithmetic   | 2.65x       | 295   | 282    | 13     |
  | math         | 0.91x       | 125   | 50     | 75     |
  | trig         | 1.10x       | 216   | 119    | 97     |
  | gradient     | 7.54x       | 22    | 22     | 0      |
  | linalg       | 3.19x       | 269   | 230    | 39     |
  | reductions   | 1.77x       | 413   | 328    | 85     |
  | manipulation | 2.25x       | 231   | 183    | 48     |
  | io           | 2.96x       | 66    | 51     | 15     |
  | indexing     | 1.29x       | 115   | 52     | 63     |
  | bitwise      | 2.02x       | 10    | 10     | 0      |
  | sorting      | 1.32x       | 75    | 45     | 30     |
  | logic        | 4.18x       | 142   | 130    | 12     |
  | statistics   | 4.00x       | 26    | 23     | 3      |
  | sets         | 2.46x       | 33    | 24     | 9      |
  | random       | 1.67x       | 46    | 41     | 5      |
  | polynomials  | 4.36x       | 27    | 21     | 6      |
  | fft          | 1.24x       | 66    | 30     | 36     |

  ### Performance by DType

  | DType      | Avg Speedup | Median Speedup | Count |
  | ---------- | ----------- | -------------- | ----- |
  | float64    | 1.90x       | 1.73x          | 288   |
  | float32    | 2.21x       | 2.02x          | 238   |
  | float16    | 2.25x       | 2.28x          | 211   |
  | int64      | 1.54x       | 1.36x          | 196   |
  | uint64     | 1.48x       | 1.35x          | 188   |
  | int32      | 2.00x       | 2.05x          | 204   |
  | uint32     | 2.03x       | 1.99x          | 189   |
  | int16      | 2.32x       | 2.69x          | 185   |
  | uint16     | 2.30x       | 2.72x          | 184   |
  | int8       | 2.74x       | 2.88x          | 185   |
  | uint8      | 2.68x       | 2.79x          | 186   |
  | complex128 | 1.88x       | 1.68x          | 68    |
  | complex64  | 1.77x       | 1.56x          | 68    |
</div>
