\n \n\n\n
\n
\n
\n
BTC/USDT live trades (Binance WebSocket)
\n
\n
\n
connecting…
\n
\n
\n
Last 5 minutes
\n
\n
\n
\n\n
Live Bitcoin Price Chart — Minimal Single-File HTML with Binance WebSocket

Create a single HTML file that displays a live, auto-updating Bitcoin (BTC/USDT) price chart. Requirements: - **Single file**: All HTML, CSS, and JavaScript in one file (CDN links are fine) - **Data source**: Binance WebSocket API (`wss://stream.binance.com:9443/ws/btcusdt@trade`) for real-time price data - **Charting**: Use Chart.js (or similar lightweight library via CDN) to render a clean line chart - **Live updates**: Chart should update in real-time as new trades come in (throttle updates to ~1 per second to keep it smooth) - **Minimal design**: Dark theme, clean typography, show current price prominently above the chart - **Time window**: Display the last 5 minutes of price data, with old points rolling off - **Responsive**: Should look good on both desktop and mobile - Keep it simple, elegant, and under 200 lines if possible

0
·OpenAI·22.6s·6:50 AM·#1
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>BTC/USDT Live (Binance)</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1"></script>
  <style>
    :root{
      --bg:#0b0f14; --panel:#0f1620; --text:#e6edf3; --muted:#8aa0b5; --accent:#4cc9f0;
      --grid:rgba(255,255,255,.06); --line:rgba(76,201,240,.9);
    }
    *{box-sizing:border-box}
    body{
      margin:0; font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;
      background:radial-gradient(1200px 600px at 20% 0%, #101a26 0%, var(--bg) 55%);
      color:var(--text);
      display:flex; min-height:100vh; align-items:center; justify-content:center; padding:18px;
    }
    .wrap{width:min(980px,100%); background:linear-gradient(180deg,var(--panel),#0b111a);
      border:1px solid rgba(255,255,255,.06); border-radius:16px; padding:16px 16px 12px;
      box-shadow:0 10px 30px rgba(0,0,0,.35);
    }
    header{display:flex; align-items:baseline; justify-content:space-between; gap:12px; padding:4px 4px 10px}
    .title{font-weight:650; letter-spacing:.2px}
    .sub{color:var(--muted); font-size:12px}
    .price{font-size:34px; font-weight:750; letter-spacing:.2px}
    .row{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap}
    .pill{font-size:12px; color:var(--muted); padding:4px 10px; border:1px solid rgba(255,255,255,.08);
      border-radius:999px; background:rgba(255,255,255,.03)
    }
    .chartBox{height:380px; position:relative}
    canvas{width:100% !important; height:100% !important}
    @media (max-width:560px){ .price{font-size:28px} .chartBox{height:320px} }
  </style>
</head>
<body>
  <div class="wrap">
    <header>
      <div>
        <div class="title">BTC/USDT <span class="sub">live trades (Binance WebSocket)</span></div>
        <div class="row">
          <div class="price" id="price"></div>
          <div class="pill" id="status">connecting…</div>
        </div>
      </div>
      <div class="sub">Last 5 minutes</div>
    </header>
    <div class="chartBox"><canvas id="c"></canvas></div>
  </div>

<script>
(() => {
  const WS_URL = "wss://stream.binance.com:9443/ws/btcusdt@trade";
  const WINDOW_MS = 5 * 60 * 1000;
  const THROTTLE_MS = 1000;

  const elPrice = document.getElementById("price");
  const elStatus = document.getElementById("status");

  const fmtUSD = new Intl.NumberFormat(undefined, { style:"currency", currency:"USD", maximumFractionDigits:2 });
  const fmtTime = ts => new Date(ts).toLocaleTimeString([], { hour:"2-digit", minute:"2-digit", second:"2-digit" });

  const ctx = document.getElementById("c").getContext("2d");
  const data = { labels: [], datasets: [{
    label: "BTC/USDT",
    data: [],
    borderColor: getComputedStyle(document.documentElement).getPropertyValue("--line").trim(),
    backgroundColor: "rgba(76,201,240,.08)",
    fill: true,
    tension: 0.25,
    pointRadius: 0,
    borderWidth: 2
  }]};

  const chart = new Chart(ctx, {
    type: "line",
    data,
    options: {
      responsive: true,
      maintainAspectRatio: false,
      animation: false,
      interaction: { mode: "index", intersect: false },
      plugins: {
        legend: { display: false },
        tooltip: {
          displayColors: false,
          callbacks: { label: (c) => `${fmtUSD.format(c.parsed.y)}` }
        }
      },
      scales: {
        x: {
          grid: { color: "rgba(255,255,255,.06)" },
          ticks: { color: "rgba(230,237,243,.7)", maxTicksLimit: 6 }
        },
        y: {
          grid: { color: "rgba(255,255,255,.06)" },
          ticks: {
            color: "rgba(230,237,243,.7)",
            callback: v => fmtUSD.format(v)
          }
        }
      }
    }
  });

  let ws, lastPrice = null, lastTs = 0, pending = false;

  function setStatus(text){ elStatus.textContent = text; }

  function pushPoint(ts, price){
    data.labels.push(fmtTime(ts));
    data.datasets[0].data.push({ x: ts, y: price });

    const cutoff = ts - WINDOW_MS;
    while (data.datasets[0].data.length && data.datasets[0].data[0].x < cutoff) {
      data.datasets[0].data.shift();
      data.labels.shift();
    }
  }

  function render(){
    pending = false;
    if (lastPrice == null) return;
    const now = Date.now();
    pushPoint(now, lastPrice);
    elPrice.textContent = fmtUSD.format(lastPrice);
    chart.update("none");
  }

  function scheduleRender(){
    if (pending) return;
    const now = Date.now();
    const wait = Math.max(0, THROTTLE_MS - (now - lastTs));
    pending = true;
    setTimeout(() => { lastTs = Date.now(); render(); }, wait);
  }

  function connect(){
    setStatus("connecting…");
    ws = new WebSocket(WS_URL);

    ws.onopen = () => setStatus("live");
    ws.onmessage = (ev) => {
      try {
        const msg = JSON.parse(ev.data);
        lastPrice = +msg.p;          // trade price as string -> number
        scheduleRender();            // throttle to ~1 update/sec
      } catch {}
    };
    ws.onclose = () => { setStatus("reconnecting…"); setTimeout(connect, 800); };
    ws.onerror = () => { try{ ws.close(); } catch {} };
  }

  // Seed chart with a flat line until first tick arrives
  const t0 = Date.now();
  for (let i = 5; i >= 1; i--) pushPoint(t0 - i*1000, NaN);
  chart.update("none");

  connect();
})();
</script>
</body>
</html>

Comments