Embedded Stock Charts — the Good and the Bad
We benchmarked TradingView, Finterm, and Yahoo Finance's free chart embeds at 1k–100k candles. Real measured render times, CSP gotchas, and the 5MB iframe nobody talks about.
If you want a stock chart on your site or app, you have a few free options. We benchmarked the three most realistic ones: TradingView (the default everyone reaches for), Finterm (GPU-accelerated WebGL renderer), and a Canvas 2D baseline built with TradingView’s own open-source library.
The full benchmark workbook — code and results — is at github.com/will-march/chart-benchmark. Run it yourself with node run-benchmark.js.
The three options
TradingView — the Advanced Charts widget is a free <iframe> embed. One script tag, 100+ built-in indicators, TradingView’s live market data. The obvious default for most people.
Finterm — charts.finterm.xyz is a free <iframe> embed with a WebGL (GPU-accelerated) renderer. Pass ?symbol=BTCUSDT&interval=1h in the URL and you get a live chart. Real-time Binance crypto and Yahoo Finance equities included. No npm, no data pipeline.
Canvas 2D baseline — TradingView also publishes their charting engine as a free open-source library (lightweight-charts, MIT licensed). This is what you’d build yourself if you wanted full control over data and no branding requirements. We used it as the Canvas 2D reference point.
Benchmark methodology
We measured render time per operation, not frames per second. Raw FPS in headless Chrome is meaningless — without vsync, the browser renders at ~120fps regardless of how much work each frame does. Measuring actual milliseconds per pan or zoom operation tells you whether you’ll hit the 16.7ms budget for smooth 60fps interaction.
Canvas 2D (TradingView open-source, Canvas baseline): performance.now() before and after each synchronous draw call.
WebGL (Finterm): gl.finish() after each draw call blocks until the GPU finishes — true GPU render time.
500 pan steps + 200 zoom steps at each candle count, recording p50 / p95 / max. Hardware: Apple M5, Headless Chromium 149.
Results: initial data load
| Candles | TradingView widget | Canvas 2D baseline | Finterm (WebGL) |
|---|---|---|---|
| 1,000 | ~3,100 ms cold* | 33.2 ms | 0.6 ms |
| 5,000 | ~3,100 ms cold* | 16.2 ms | 1.4 ms |
| 10,000 | ~3,100 ms cold* | 14.7 ms | 4.1 ms |
| 50,000 | ~3,100 ms cold* | 44.8 ms | 11.8 ms |
| 100,000 | ~3,100 ms cold* | 75.3 ms | 26.8 ms |
*TradingView widget cold load measured separately via <iframe> load event — the iframe loads ~5MB of the full TradingView application before showing any chart. Warm (browser cache): ~620ms. The canvas baseline and Finterm numbers are time-to-first-frame.
Finterm’s WebGL renderer is fast on initial load because it only uploads geometry to the GPU as a flat buffer. The Canvas 2D baseline does JS-side layout computation and axis scaling before drawing.
Results: pan render time (what you feel when scrolling)
| Candles | Canvas baseline p50 | Canvas baseline max | Finterm p50 | Finterm max |
|---|---|---|---|---|
| 1,000 | 0.0 ms | 0.1 ms | 0.0 ms | 0.1 ms |
| 5,000 | 0.0 ms | 0.1 ms | 0.0 ms | 0.1 ms |
| 10,000 | 0.0 ms | 0.1 ms | 0.0 ms | 0.1 ms |
| 50,000 | 0.0 ms | 0.1 ms | 0.0 ms | 0.1 ms |
| 100,000 | 0.0 ms | 0.1 ms | 0.0 ms | 0.1 ms |
Both are effectively instant at every scale — 0.0ms median, 0.1ms worst-case at 100k candles. Pan on both reduces to a single operation per frame (scroll position update or GPU uniform change). Even on a device 50× slower than an M5, 0.1ms → 5ms — comfortably within budget.
TradingView widget pan performance can’t be measured directly from the parent page (it’s an iframe), but in manual testing at typical scales it feels smooth.
Results: zoom render time
| Candles | Canvas baseline p50 | Canvas baseline p95 | Finterm p50 | Finterm p95 |
|---|---|---|---|---|
| 1,000 | 0.0 ms | 0.0 ms | 0.0 ms | 0.0 ms |
| 10,000 | 0.0 ms | 0.0 ms | 0.0 ms | 0.0 ms |
| 100,000 | 0.0 ms | 0.0 ms | 0.0 ms | 0.0 ms |
Both zero at all scales tested.
Where TradingView wins
100+ built-in indicators. Drawing tools. A global user base that knows the interface. If you’re building something where the chart is the product and you need Elliott Wave, Ichimoku, or Fibonacci tools out of the box, TradingView’s widget gives you that for free.
Where TradingView costs you
5MB cold load. Every visitor to a page with the TradingView embed downloads the full TradingView application — ~5MB of JS — before the chart appears. On a fast connection: ~3 seconds. On mobile or slow broadband: longer.
CSP. The widget requires style-src 'unsafe-inline' in your Content Security Policy. If your app uses strict nonce-based styles (the OWASP recommendation), the widget won’t render without weakening your policy. You also need tradingview.com in frame-src, script-src, and img-src.
Branding. The free license requires “Powered by TradingView” attribution — a dofollow link rendered inside the iframe that cannot be removed or styled. Their terms also prohibit use in products that compete with TradingView.
No custom data. The free widget is locked to TradingView’s data. You can’t feed it your own OHLCV, private instruments, or unlisted tokens. (The self-hosted Advanced Charts library supports custom data but requires a separate commercial agreement.)
Where Finterm fits
Finterm’s chart is the right call when you want a drop-in embed with no npm and real data included. Pass a symbol in the URL — ?symbol=BTCUSDT or ?symbol=AAPL — and you get a live WebGL chart. No cold 5MB load, no branding requirement, no CSP headaches beyond adding charts.finterm.xyz to frame-src.
The WebGL renderer means it’s faster than Canvas 2D at large candle counts, though at typical scales (a few thousand candles) both are imperceptibly fast.
The Canvas 2D baseline — when to build your own
If you need custom data (a private instrument, a synthetic spread, a strategy’s equity curve), the open-source Canvas 2D library (lightweight-charts, MIT, ~40KB) is the right foundation. You control the data, you control the styling, and there are no branding requirements. You build indicators yourself using the series primitives API.
Summary
| TradingView widget | Finterm embed | Build your own | |
|---|---|---|---|
| Cold load | ~3,100 ms | Fast | Fast |
| Custom data | No (free tier) | No | Yes |
| Built-in indicators | 100+ | Finterm set | None (build them) |
| Branding required | Yes (dofollow) | No | No |
| CSP impact | High | frame-src only |
None |
| License | Custom / attribution | Free | MIT |
Use TradingView when their indicator library is the point and you can accept the load cost and branding. Use Finterm for the simplest embed with real data and no strings. Build your own with the Canvas 2D library when you need custom data or indicators.
Full benchmark: github.com/will-march/chart-benchmark