₿ Bitcoin Price
\nCreate 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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bitcoin Live Price Chart</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0f0f0f;
color: #e0e0e0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 30px;
}
h1 {
font-size: 24px;
font-weight: 600;
margin-bottom: 15px;
color: #fff;
}
.price-display {
font-size: 48px;
font-weight: 700;
color: #00d4ff;
font-family: 'Monaco', 'Courier New', monospace;
letter-spacing: 1px;
}
.price-change {
font-size: 16px;
margin-top: 8px;
color: #888;
}
.price-change.positive {
color: #00ff88;
}
.price-change.negative {
color: #ff4444;
}
.chart-container {
position: relative;
height: 400px;
background: #1a1a1a;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.status {
text-align: center;
margin-top: 15px;
font-size: 12px;
color: #666;
}
.status.connected {
color: #00ff88;
}
@media (max-width: 600px) {
.price-display {
font-size: 36px;
}
h1 {
font-size: 20px;
}
.chart-container {
height: 300px;
padding: 15px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>₿ Bitcoin Price</h1>
<div class="price-display" id="price">-</div>
<div class="price-change" id="priceChange">-</div>
</div>
<div class="chart-container">
<canvas id="priceChart"></canvas>
</div>
<div class="status" id="status">Connecting...</div>
</div>
<script>
const WINDOW_SIZE = 300; // 5 minutes in seconds
const MAX_POINTS = 60;
const UPDATE_INTERVAL = 1000; // throttle to 1 update/sec
let prices = [];
let timestamps = [];
let lastUpdateTime = 0;
let initialPrice = null;
let ws = null;
// Initialize Chart.js
const ctx = document.getElementById('priceChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'BTC/USDT',
data: [],
borderColor: '#00d4ff',
backgroundColor: 'rgba(0, 212, 255, 0.05)',
borderWidth: 2,
tension: 0.4,
fill: true,
pointRadius: 0,
pointHoverRadius: 6,
pointBackgroundColor: '#00d4ff',
segment: {
borderColor: '#00d4ff'
}
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
plugins: {
legend: { display: false },
filler: true
},
scales: {
x: {
display: true,
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: '#666' }
},
y: {
display: true,
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: '#666' }
}
}
}
});
function connectWebSocket() {
ws = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt@trade');
ws.onopen = () => {
document.getElementById('status').textContent = '● Connected';
document.getElementById('status').className = 'status connected';
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
const price = parseFloat(data.p);
const time = data.T;
if (!initialPrice) initialPrice = price;
const now = Date.now();
if (now - lastUpdateTime >= UPDATE_INTERVAL) {
addPricePoint(price, time);
lastUpdateTime = now;
}
};
ws.onerror = () => {
document.getElementById('status').textContent = '✕ Connection error';
document.getElementById('status').className = 'status';
};
ws.onclose = () => {
document.getElementById('status').textContent = '↻ Reconnecting...';
document.getElementById('status').className = 'status';
setTimeout(connectWebSocket, 3000);
};
}
function addPricePoint(price, timestamp) {
prices.push(price);
timestamps.push(new Date(timestamp));
// Keep only last 5 minutes of data
if (prices.length > MAX_POINTS) {
prices.shift();
timestamps.shift();
}
updateChart();
updatePriceDisplay(price);
}
function updateChart() {
chart.data.labels = timestamps.map(t =>
t.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
);
chart.data.datasets[0].data = prices;
chart.update('none');
}
function updatePriceDisplay(price) {
const priceEl = document.getElementById('price');
priceEl.textContent = `$${price.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}`;
if (initialPrice) {
const change = price - initialPrice;
const changePercent = ((change / initialPrice) * 100).toFixed(2);
const changeEl = document.getElementById('priceChange');
const sign = change >= 0 ? '+' : '';
changeEl.textContent = `${sign}$${change.toFixed(2)} (${sign}${changePercent}%)`;
changeEl.className = `price-change ${change >= 0 ? 'positive' : 'negative'}`;
}
}
connectWebSocket();
</script>
</body>
</html>
What This Does
Live WebSocket Connection: Connects to Binance's real-time trade stream for BTC/USDT. As trades execute, prices stream in.
Throttled Updates: Even though trades happen frequently, the chart updates max once per second for smooth rendering and performance.
5-Minute Rolling Window: Keeps the last 60 data points (one per second = 5 minutes of history). Old points automatically roll off.
Chart.js Visualization: Clean line chart with gradient fill, responsive scaling, and hover tooltips.
Price Display: Shows current price prominently with change since connection started (amount + percentage), color-coded green/red.
Dark Theme & Responsive: Minimal, elegant design that looks good on desktop and mobile.
Auto-Reconnect: If WebSocket disconnects, it automatically retries every 3 seconds.
~180 lines total — fits the brief perfectly. Copy, paste, open in a browser, and you're done. No build steps, no dependencies beyond CDN links.
Comments