Skip to content

Commit fd23b0e

Browse files
author
Panos
committed
Add live interactive demo
1 parent c7ed162 commit fd23b0e

File tree

2 files changed

+358
-6
lines changed

2 files changed

+358
-6
lines changed

docs/demo.html

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>GPU Hot - Interactive Demo</title>
7+
8+
<!-- External Libraries -->
9+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11+
12+
<!-- Embed the exact CSS from the real application via CDN -->
13+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/psalias2006/gpu-hot@main/static/css/styles.css">
14+
15+
<style>
16+
/* Demo-specific notice banner */
17+
.demo-notice {
18+
background: rgba(255, 193, 7, 0.15);
19+
border: 1px solid rgba(255, 193, 7, 0.3);
20+
border-radius: 12px;
21+
padding: 1rem 1.5rem;
22+
margin-bottom: 2rem;
23+
text-align: center;
24+
color: #ffc107;
25+
font-size: 0.9rem;
26+
font-weight: 600;
27+
}
28+
</style>
29+
</head>
30+
<body>
31+
<div class="container">
32+
<div class="header">
33+
<h1>🔥 GPU Hot</h1>
34+
<p>Real-time NVIDIA GPU monitoring dashboard</p>
35+
</div>
36+
37+
<div class="demo-notice">
38+
⚠️ Interactive Demo - Simulated Data
39+
</div>
40+
41+
<div class="status-bar">
42+
<div class="status-indicator">
43+
<div class="status-dot"></div>
44+
<span>Live Monitoring (Demo)</span>
45+
</div>
46+
<div id="connection-status">Running Demo</div>
47+
</div>
48+
49+
<!-- View Selector -->
50+
<div class="view-selector" id="view-selector">
51+
<button class="view-option active" data-view="overview" onclick="switchToView('overview')">All GPUs</button>
52+
</div>
53+
54+
<!-- Overview Tab -->
55+
<div id="tab-overview" class="tab-content active">
56+
<div id="overview-container" class="overview-grid">
57+
</div>
58+
</div>
59+
60+
<!-- Individual GPU Tabs (dynamically created) -->
61+
62+
<div class="processes-section">
63+
<div class="processes-header" onclick="toggleProcesses()">
64+
<div class="processes-title">
65+
Active GPU Processes
66+
</div>
67+
<div class="processes-toggle">
68+
<span id="process-count">0 processes</span>
69+
<span class="toggle-icon"></span>
70+
</div>
71+
</div>
72+
<div class="processes-content" id="processes-content">
73+
<div class="processes-inner" id="processes-container">
74+
</div>
75+
</div>
76+
</div>
77+
78+
<div class="system-info">
79+
<div class="system-metric">
80+
<canvas class="system-metric-chart" id="cpu-chart"></canvas>
81+
<div class="system-metric-value" id="cpu-usage">0%</div>
82+
<div class="system-metric-label">System CPU</div>
83+
<div class="metric-sublabel">Host Processor</div>
84+
</div>
85+
<div class="system-metric">
86+
<canvas class="system-metric-chart" id="memory-chart"></canvas>
87+
<div class="system-metric-value" id="memory-usage">0%</div>
88+
<div class="system-metric-label">System RAM</div>
89+
<div class="metric-sublabel">Host Memory</div>
90+
</div>
91+
</div>
92+
</div>
93+
94+
<!-- Load the EXACT same JavaScript files from the real application via CDN -->
95+
<script src="https://cdn.jsdelivr.net/gh/psalias2006/gpu-hot@main/static/js/charts.js"></script>
96+
<script src="https://cdn.jsdelivr.net/gh/psalias2006/gpu-hot@main/static/js/gpu-cards.js"></script>
97+
<script src="https://cdn.jsdelivr.net/gh/psalias2006/gpu-hot@main/static/js/ui.js"></script>
98+
99+
<!-- Demo Data Generator (replaces socket-handlers.js) -->
100+
<script>
101+
// ============================================================================
102+
// DEMO DATA GENERATOR - Simulates realistic GPU data
103+
// ============================================================================
104+
105+
const GPU_NAMES = [
106+
'NVIDIA GeForce RTX 4060 Ti',
107+
'NVIDIA GeForce RTX 4080',
108+
];
109+
110+
const PROCESS_NAMES = [
111+
'python3',
112+
'pytorch_train.py',
113+
'tensorflow',
114+
'chrome',
115+
'code',
116+
];
117+
118+
class DemoDataGenerator {
119+
constructor(numGPUs = 1) {
120+
this.numGPUs = numGPUs;
121+
this.gpuStates = [];
122+
this.time = 0;
123+
124+
// Initialize GPU states - realistic idle behavior
125+
for (let i = 0; i < numGPUs; i++) {
126+
this.gpuStates.push({
127+
baseUtil: 0 + Math.random() * 5, // Very low idle utilization
128+
baseTemp: 30 + Math.random() * 5, // Cool at idle
129+
basePower: 10 + Math.random() * 10, // Low idle power
130+
baseMemUsed: 500 + Math.random() * 100, // Minimal memory at idle
131+
memTotal: 16384,
132+
totalEnergy: 0
133+
});
134+
}
135+
}
136+
137+
generateGPUData() {
138+
this.time += 1;
139+
const gpus = {};
140+
141+
for (let i = 0; i < this.numGPUs; i++) {
142+
const state = this.gpuStates[i];
143+
144+
// Simulate realistic idle with occasional small spikes
145+
const noise = () => (Math.random() - 0.5) * 3;
146+
const wave = Math.sin(this.time * 0.03 + i) * 8;
147+
148+
const utilization = Math.max(0, Math.min(100, state.baseUtil + wave + noise()));
149+
const temperature = Math.max(25, Math.min(85, 31 + utilization * 0.2 + noise() * 0.3));
150+
151+
// Power scales with utilization
152+
const idlePower = 11.5;
153+
const powerDraw = utilization > 3 ?
154+
Math.max(idlePower, Math.min(165, idlePower + (utilization / 100) * 150 + wave + noise() * 2)) :
155+
idlePower;
156+
157+
const memoryUsed = Math.max(512, Math.min(state.memTotal, state.baseMemUsed + wave * 20 + noise() * 50));
158+
159+
// Clock speeds - idle clocks when idle
160+
const idleGraphicsClock = 210;
161+
const maxGraphicsClock = 2610;
162+
const idleMemoryClock = 405;
163+
const maxMemoryClock = 10501;
164+
165+
const clockGraphics = utilization > 3 ?
166+
Math.round(idleGraphicsClock + (utilization / 100) * (maxGraphicsClock - idleGraphicsClock)) :
167+
idleGraphicsClock;
168+
169+
const clockMemory = utilization > 3 ?
170+
Math.round(idleMemoryClock + (utilization / 100) * (maxMemoryClock - idleMemoryClock)) :
171+
idleMemoryClock;
172+
173+
const clockSm = Math.round(clockGraphics * 1.0);
174+
const clockSmMax = 3105;
175+
const clockVideo = Math.round(clockGraphics * 0.95);
176+
177+
// PCIe link adjusts with utilization
178+
const pcieGen = utilization > 10 ? 4 : utilization > 5 ? 2 : 1;
179+
const pcieWidth = utilization > 10 ? 16 : 8;
180+
181+
// Energy accumulation (Wh)
182+
if (!state.totalEnergy) state.totalEnergy = 0;
183+
state.totalEnergy += (powerDraw * (0.5 / 3600)); // 500ms interval, in Wh
184+
185+
// BAR1 memory (small portion of total)
186+
const bar1Used = Math.round(memoryUsed * 0.0004);
187+
const bar1Total = Math.round(state.memTotal * 0.016);
188+
189+
// Process counts
190+
const computeProcesses = utilization > 20 ? Math.floor(utilization / 30) : 0;
191+
const graphicsProcesses = utilization > 10 ? Math.floor(Math.random() * 2) : utilization > 5 ? 2 : 0;
192+
193+
gpus[i] = {
194+
name: GPU_NAMES[i % GPU_NAMES.length],
195+
utilization: Math.round(utilization),
196+
temperature: Math.round(temperature),
197+
memory_used: Math.round(memoryUsed),
198+
memory_total: state.memTotal,
199+
power_draw: powerDraw,
200+
power_limit: 165,
201+
fan_speed: temperature < 40 ? 0 : Math.round(Math.max(0, Math.min(100, (temperature - 35) * 2))),
202+
clock_graphics: clockGraphics,
203+
clock_memory: clockMemory,
204+
clock_sm: clockSm,
205+
clock_sm_max: clockSmMax,
206+
clock_video: clockVideo,
207+
memory_utilization: Math.round(utilization * 0.7),
208+
pcie_gen: pcieGen,
209+
pcie_width: pcieWidth,
210+
pcie_link_gen_max: 4,
211+
pcie_link_width_max: 8,
212+
pcie_tx_throughput: Math.round((utilization / 100) * 50 + Math.random() * 5),
213+
pcie_rx_throughput: Math.round((utilization / 100) * 30 + Math.random() * 3),
214+
performance_state: utilization > 70 ? 'P0' : utilization > 40 ? 'P2' : utilization > 3 ? 'P5' : 'P8',
215+
throttle_reasons: utilization > 90 ? 'Active' : 'None',
216+
driver_version: '550.163.01',
217+
energy_consumption_wh: state.totalEnergy, // Already in Wh
218+
brand: 'GRID',
219+
architecture: 'Ada Lovelace',
220+
memory_free: state.memTotal - Math.round(memoryUsed),
221+
bar1_memory_used: bar1Used,
222+
bar1_memory_total: bar1Total,
223+
compute_processes_count: computeProcesses,
224+
graphics_processes_count: graphicsProcesses,
225+
pcie_gen_max: 4,
226+
pcie_width_max: 8,
227+
_fallback_mode: false
228+
};
229+
}
230+
231+
return gpus;
232+
}
233+
234+
generateProcesses() {
235+
const numProcesses = Math.floor(Math.random() * 3);
236+
const processes = [];
237+
238+
for (let i = 0; i < numProcesses; i++) {
239+
processes.push({
240+
pid: 1000 + Math.floor(Math.random() * 30000),
241+
name: PROCESS_NAMES[Math.floor(Math.random() * PROCESS_NAMES.length)],
242+
memory: Math.round(500 + Math.random() * 3000)
243+
});
244+
}
245+
246+
return processes;
247+
}
248+
249+
generateSystemInfo() {
250+
const cpuBase = 10 + Math.random() * 20;
251+
const cpuNoise = (Math.random() - 0.5) * 10;
252+
const memBase = 30 + Math.random() * 20;
253+
254+
return {
255+
cpu_percent: Math.max(5, Math.min(95, cpuBase + cpuNoise)),
256+
memory_percent: Math.max(20, Math.min(85, memBase + (Math.random() - 0.5) * 5))
257+
};
258+
}
259+
260+
generateFullData() {
261+
return {
262+
gpus: this.generateGPUData(),
263+
processes: this.generateProcesses(),
264+
system: this.generateSystemInfo()
265+
};
266+
}
267+
}
268+
269+
// ============================================================================
270+
// DEMO APPLICATION - Uses the same update functions as the real app
271+
// ============================================================================
272+
273+
const dataGenerator = new DemoDataGenerator(1); // 1 GPU for demo
274+
275+
function updateDemo() {
276+
const data = dataGenerator.generateFullData();
277+
278+
// Use the EXACT same update functions from the real application
279+
handleGPUDataUpdate(data);
280+
}
281+
282+
// This function mimics what socket-handlers.js does
283+
function handleGPUDataUpdate(data) {
284+
const overviewContainer = document.getElementById('overview-container');
285+
286+
if (!data.gpus || Object.keys(data.gpus).length === 0) {
287+
overviewContainer.innerHTML = '<div class="loading">No GPU data available</div>';
288+
return;
289+
}
290+
291+
// Update each GPU
292+
Object.keys(data.gpus).forEach(gpuId => {
293+
const gpuInfo = data.gpus[gpuId];
294+
295+
// Initialize chart data if needed
296+
if (!chartData[gpuId]) {
297+
initGPUData(gpuId);
298+
}
299+
300+
// Create or update overview card
301+
const existingOverview = overviewContainer.querySelector(`[data-gpu-id="${gpuId}"]`);
302+
if (!existingOverview) {
303+
overviewContainer.insertAdjacentHTML('beforeend', createOverviewCard(gpuId, gpuInfo));
304+
initOverviewMiniChart(gpuId, gpuInfo.utilization);
305+
} else {
306+
updateOverviewCard(gpuId, gpuInfo, true);
307+
}
308+
309+
// Create or update detailed GPU tab
310+
ensureGPUTab(gpuId, gpuInfo, true);
311+
});
312+
313+
// Update processes
314+
if (data.processes) {
315+
updateProcesses(data.processes);
316+
}
317+
318+
// Update system info
319+
if (data.system) {
320+
updateSystemInfo(data.system);
321+
}
322+
323+
// Auto-switch to single GPU view on first load
324+
const gpuCount = Object.keys(data.gpus).length;
325+
const gpuIds = Object.keys(data.gpus);
326+
autoSwitchSingleGPU(gpuCount, gpuIds);
327+
}
328+
329+
// Initialize and start demo
330+
document.addEventListener('DOMContentLoaded', function() {
331+
console.log('GPU Hot Demo initialized');
332+
333+
// Initial update
334+
updateDemo();
335+
336+
// Update every 500ms (same as real app)
337+
setInterval(updateDemo, 500);
338+
339+
// Initialize system charts
340+
initSystemCharts();
341+
});
342+
</script>
343+
</body>
344+
</html>

docs/index.html

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,12 +616,20 @@
616616
<section class="hero">
617617
<h1>GPU Hot<br>Metrics in Seconds</h1>
618618
<p>Real-time GPU monitoring in your browser. Start with one server, scale to dozens. No infrastructure, no setup, no SSH. Just one command.</p>
619-
<a href="https://github.com/psalias2006/gpu-hot" class="cta">
620-
View on GitHub
621-
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
622-
<path d="M7 17L17 7M17 7v10M17 7H7"/>
623-
</svg>
624-
</a>
619+
<div style="display: flex; gap: 1rem; flex-wrap: wrap;">
620+
<a href="./demo.html" class="cta" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; border: none;">
621+
Try Interactive Demo
622+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
623+
<path d="M5 12h14M12 5l7 7-7 7"/>
624+
</svg>
625+
</a>
626+
<a href="https://github.com/psalias2006/gpu-hot" class="cta">
627+
View on GitHub
628+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
629+
<path d="M7 17L17 7M17 7v10M17 7H7"/>
630+
</svg>
631+
</a>
632+
</div>
625633
</section>
626634

627635
<!-- Terminal -->

0 commit comments

Comments
 (0)