cpu-fan-simulator / index.html
PrinzPesia's picture
Wichtig die Temperatur soll langsamer ansteigen bei CPU Auslastung. - Initial Deployment
dc02922 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CPU Fan Simulator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<style>
.fan-blades {
animation: spin linear infinite;
transform-origin: center;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.temp-indicator {
transition: all 0.5s ease;
}
.slider-thumb::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #3b82f6;
cursor: pointer;
}
.slider-thumb::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #3b82f6;
cursor: pointer;
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen">
<div class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold text-center mb-8 text-blue-400">CPU Fan Simulator</h1>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- CPU Visualization -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg col-span-1">
<h2 class="text-2xl font-semibold mb-4 text-center">CPU Status</h2>
<div class="flex flex-col items-center">
<div class="relative w-48 h-48 mb-6">
<!-- CPU Chip -->
<div class="absolute inset-0 bg-gray-700 rounded-lg flex items-center justify-center">
<div class="w-32 h-32 bg-gray-600 rounded-md flex items-center justify-center">
<div class="text-center">
<div id="cpuTemp" class="text-3xl font-bold">35°C</div>
<div id="cpuLoad" class="text-lg">0% Load</div>
</div>
</div>
</div>
<!-- Fan -->
<div id="fan" class="absolute -bottom-8 left-1/2 transform -translate-x-1/2 w-24 h-24">
<svg viewBox="0 0 100 100" class="w-full h-full">
<circle cx="50" cy="50" r="45" fill="#4b5563" />
<g class="fan-blades" id="fanBlades">
<path d="M50,5 L55,45 L50,50 L45,45 Z" fill="#9ca3af" />
<path d="M95,50 L55,55 L50,50 L55,45 Z" fill="#9ca3af" />
<path d="M50,95 L45,55 L50,50 L55,55 Z" fill="#9ca3af" />
<path d="M5,50 L45,45 L50,50 L45,55 Z" fill="#9ca3af" />
</g>
<circle cx="50" cy="50" r="15" fill="#374151" />
</svg>
</div>
</div>
<!-- Temperature indicator -->
<div class="w-full h-6 bg-gradient-to-r from-blue-500 via-yellow-500 to-red-500 rounded-full mb-2 relative">
<div id="tempIndicator" class="temp-indicator absolute top-0 left-0 w-2 h-6 bg-white rounded-full -ml-1"></div>
</div>
<div class="flex justify-between w-full text-xs">
<span>0°C</span>
<span>50°C</span>
<span>100°C</span>
</div>
<!-- Fan speed -->
<div class="mt-6 w-full">
<div class="flex justify-between mb-1">
<span>Fan Speed</span>
<span id="fanSpeedDisplay">0%</span>
</div>
<div class="w-full h-4 bg-gray-700 rounded-full overflow-hidden">
<div id="fanSpeedBar" class="h-full bg-blue-500 rounded-full" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<!-- Controls -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg col-span-1">
<h2 class="text-2xl font-semibold mb-4 text-center">Controls</h2>
<div class="space-y-6">
<!-- CPU Load Slider -->
<div>
<label for="cpuLoadSlider" class="block mb-2">CPU Load: <span id="cpuLoadValue">0</span>%</label>
<input type="range" id="cpuLoadSlider" min="0" max="100" value="0" class="w-full slider-thumb">
</div>
<!-- Max Temp Slider -->
<div>
<label for="maxTempSlider" class="block mb-2">Max Safe Temperature: <span id="maxTempValue">85</span>°C</label>
<input type="range" id="maxTempSlider" min="60" max="100" value="85" class="w-full slider-thumb">
</div>
<!-- Fan Curve Type -->
<div>
<label class="block mb-2">Fan Curve Type:</label>
<div class="grid grid-cols-2 gap-2">
<button id="curveLinear" class="bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded">Linear</button>
<button id="curveAggressive" class="bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded">Aggressive</button>
<button id="curveSilent" class="bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded">Silent</button>
<button id="curveCustom" class="bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded">Custom</button>
</div>
</div>
<!-- Fan Speed Limit -->
<div>
<label for="maxFanSpeedSlider" class="block mb-2">Max Fan Speed: <span id="maxFanSpeedValue">100</span>%</label>
<input type="range" id="maxFanSpeedSlider" min="20" max="100" value="100" class="w-full slider-thumb">
</div>
<!-- Buttons -->
<div class="grid grid-cols-2 gap-4 pt-4">
<button id="startStress" class="bg-red-600 hover:bg-red-700 py-3 px-4 rounded-lg font-medium">
<i class="fas fa-bolt mr-2"></i> Stress Test
</button>
<button id="stopStress" class="bg-gray-600 hover:bg-gray-500 py-3 px-4 rounded-lg font-medium">
<i class="fas fa-stop mr-2"></i> Stop
</button>
</div>
</div>
</div>
<!-- Fan Curve Editor -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg col-span-1 lg:col-span-1">
<h2 class="text-2xl font-semibold mb-4 text-center">Fan Curve</h2>
<div class="h-64 mb-4">
<canvas id="fanCurveChart"></canvas>
</div>
<div id="customCurveControls" class="space-y-4 opacity-50 pointer-events-none">
<div>
<label for="lowTemp" class="block mb-2">Low Temp (<span id="lowTempValue">30</span>°C):</label>
<input type="range" id="lowTemp" min="20" max="50" value="30" class="w-full slider-thumb">
</div>
<div>
<label for="lowSpeed" class="block mb-2">Low Speed (<span id="lowSpeedValue">20</span>%):</label>
<input type="range" id="lowSpeed" min="0" max="50" value="20" class="w-full slider-thumb">
</div>
<div>
<label for="highTemp" class="block mb-2">High Temp (<span id="highTempValue">70</span>°C):</label>
<input type="range" id="highTemp" min="50" max="90" value="70" class="w-full slider-thumb">
</div>
<div>
<label for="highSpeed" class="block mb-2">High Speed (<span id="highSpeedValue">80</span>%):</label>
<input type="range" id="highSpeed" min="50" max="100" value="80" class="w-full slider-thumb">
</div>
</div>
</div>
</div>
<!-- Logs -->
<div class="bg-gray-800 rounded-xl p-6 shadow-lg mt-8">
<h2 class="text-2xl font-semibold mb-4">System Log</h2>
<div id="systemLog" class="h-40 overflow-y-auto bg-gray-900 p-3 rounded font-mono text-sm space-y-1">
<div class="text-gray-400">System initialized. Ready to simulate CPU fan behavior.</div>
</div>
</div>
</div>
<script>
// State variables
let cpuLoad = 0;
let cpuTemp = 35;
let maxTemp = 85;
let fanSpeed = 0;
let maxFanSpeed = 100;
let stressTestInterval = null;
let fanCurveType = 'linear';
let fanCurvePoints = [];
// DOM elements
const cpuTempElement = document.getElementById('cpuTemp');
const cpuLoadElement = document.getElementById('cpuLoad');
const cpuLoadSlider = document.getElementById('cpuLoadSlider');
const cpuLoadValue = document.getElementById('cpuLoadValue');
const maxTempSlider = document.getElementById('maxTempSlider');
const maxTempValue = document.getElementById('maxTempValue');
const maxFanSpeedSlider = document.getElementById('maxFanSpeedSlider');
const maxFanSpeedValue = document.getElementById('maxFanSpeedValue');
const fanSpeedDisplay = document.getElementById('fanSpeedDisplay');
const fanSpeedBar = document.getElementById('fanSpeedBar');
const tempIndicator = document.getElementById('tempIndicator');
const fanBlades = document.getElementById('fanBlades');
const startStress = document.getElementById('startStress');
const stopStress = document.getElementById('stopStress');
const systemLog = document.getElementById('systemLog');
// Fan curve buttons
const curveLinear = document.getElementById('curveLinear');
const curveAggressive = document.getElementById('curveAggressive');
const curveSilent = document.getElementById('curveSilent');
const curveCustom = document.getElementById('curveCustom');
// Custom curve controls
const customCurveControls = document.getElementById('customCurveControls');
const lowTemp = document.getElementById('lowTemp');
const lowTempValue = document.getElementById('lowTempValue');
const lowSpeed = document.getElementById('lowSpeed');
const lowSpeedValue = document.getElementById('lowSpeedValue');
const highTemp = document.getElementById('highTemp');
const highTempValue = document.getElementById('highTempValue');
const highSpeed = document.getElementById('highSpeed');
const highSpeedValue = document.getElementById('highSpeedValue');
// Chart
const ctx = document.getElementById('fanCurveChart').getContext('2d');
let fanCurveChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: 'Fan Speed (%)',
data: [],
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'Temperature (°C)',
color: '#fff'
},
min: 0,
max: 100,
ticks: {
color: '#fff'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
},
y: {
title: {
display: true,
text: 'Fan Speed (%)',
color: '#fff'
},
min: 0,
max: 100,
ticks: {
color: '#fff'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
},
plugins: {
legend: {
labels: {
color: '#fff'
}
},
tooltip: {
callbacks: {
label: function(context) {
return `Fan Speed: ${context.parsed.y}% at ${context.parsed.x}°C`;
}
}
}
}
}
});
// Initialize
updateFanCurve();
updateUI();
// Event listeners
cpuLoadSlider.addEventListener('input', function() {
cpuLoad = parseInt(this.value);
cpuLoadValue.textContent = cpuLoad;
updateCPUState();
updateUI();
});
maxTempSlider.addEventListener('input', function() {
maxTemp = parseInt(this.value);
maxTempValue.textContent = maxTemp;
updateFanCurve();
updateUI();
});
maxFanSpeedSlider.addEventListener('input', function() {
maxFanSpeed = parseInt(this.value);
maxFanSpeedValue.textContent = maxFanSpeed;
updateFanCurve();
updateUI();
});
startStress.addEventListener('click', function() {
if (stressTestInterval) clearInterval(stressTestInterval);
cpuLoad = 100;
cpuLoadSlider.value = 100;
cpuLoadValue.textContent = '100';
stressTestInterval = setInterval(function() {
updateCPUState();
updateUI();
}, 500);
addLog("Started CPU stress test");
});
stopStress.addEventListener('click', function() {
if (stressTestInterval) {
clearInterval(stressTestInterval);
stressTestInterval = null;
}
cpuLoad = 0;
cpuLoadSlider.value = 0;
cpuLoadValue.textContent = '0';
updateCPUState();
updateUI();
addLog("Stopped CPU stress test");
});
// Fan curve type buttons
curveLinear.addEventListener('click', function() {
fanCurveType = 'linear';
updateButtonStates();
updateFanCurve();
addLog("Fan curve set to Linear");
});
curveAggressive.addEventListener('click', function() {
fanCurveType = 'aggressive';
updateButtonStates();
updateFanCurve();
addLog("Fan curve set to Aggressive");
});
curveSilent.addEventListener('click', function() {
fanCurveType = 'silent';
updateButtonStates();
updateFanCurve();
addLog("Fan curve set to Silent");
});
curveCustom.addEventListener('click', function() {
fanCurveType = 'custom';
updateButtonStates();
updateFanCurve();
addLog("Fan curve set to Custom");
});
// Custom curve controls
lowTemp.addEventListener('input', function() {
lowTempValue.textContent = this.value;
if (fanCurveType === 'custom') updateFanCurve();
});
lowSpeed.addEventListener('input', function() {
lowSpeedValue.textContent = this.value;
if (fanCurveType === 'custom') updateFanCurve();
});
highTemp.addEventListener('input', function() {
highTempValue.textContent = this.value;
if (fanCurveType === 'custom') updateFanCurve();
});
highSpeed.addEventListener('input', function() {
highSpeedValue.textContent = this.value;
if (fanCurveType === 'custom') updateFanCurve();
});
// Functions
function updateButtonStates() {
curveLinear.className = fanCurveType === 'linear' ?
'bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded' :
'bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded';
curveAggressive.className = fanCurveType === 'aggressive' ?
'bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded' :
'bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded';
curveSilent.className = fanCurveType === 'silent' ?
'bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded' :
'bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded';
curveCustom.className = fanCurveType === 'custom' ?
'bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded' :
'bg-gray-700 hover:bg-gray-600 py-2 px-4 rounded';
if (fanCurveType === 'custom') {
customCurveControls.className = 'space-y-4';
customCurveControls.classList.remove('opacity-50', 'pointer-events-none');
} else {
customCurveControls.className = 'space-y-4 opacity-50 pointer-events-none';
}
}
function updateCPUState() {
// Simulate temperature based on load with slower response
const targetTemp = 30 + (cpuLoad * 0.7); // Base temp + load impact
const tempChange = (targetTemp - cpuTemp) * 0.03; // Much more gradual change
cpuTemp = Math.min(100, Math.max(30, cpuTemp + tempChange));
// Calculate fan speed based on curve
calculateFanSpeed();
}
function calculateFanSpeed() {
let newFanSpeed = 0;
if (fanCurveType === 'linear') {
// Linear curve from 30°C (0%) to maxTemp (100%)
const minTemp = 30;
const maxTempEffective = maxTemp;
if (cpuTemp <= minTemp) {
newFanSpeed = 0;
} else if (cpuTemp >= maxTempEffective) {
newFanSpeed = 100;
} else {
newFanSpeed = ((cpuTemp - minTemp) / (maxTempEffective - minTemp)) * 100;
}
}
else if (fanCurveType === 'aggressive') {
// More aggressive curve that ramps up quickly
const minTemp = 30;
const midTemp = 50;
const maxTempEffective = maxTemp;
if (cpuTemp <= minTemp) {
newFanSpeed = 0;
} else if (cpuTemp <= midTemp) {
newFanSpeed = 20 + ((cpuTemp - minTemp) / (midTemp - minTemp)) * 30;
} else if (cpuTemp >= maxTempEffective) {
newFanSpeed = 100;
} else {
newFanSpeed = 50 + ((cpuTemp - midTemp) / (maxTempEffective - midTemp)) * 50;
}
}
else if (fanCurveType === 'silent') {
// Silent curve that keeps fan low as long as possible
const minTemp = 30;
const midTemp = 70;
const maxTempEffective = maxTemp;
if (cpuTemp <= minTemp) {
newFanSpeed = 0;
} else if (cpuTemp <= midTemp) {
newFanSpeed = ((cpuTemp - minTemp) / (midTemp - minTemp)) * 40;
} else if (cpuTemp >= maxTempEffective) {
newFanSpeed = 100;
} else {
newFanSpeed = 40 + ((cpuTemp - midTemp) / (maxTempEffective - midTemp)) * 60;
}
}
else if (fanCurveType === 'custom') {
// Custom curve based on user settings
const customLowTemp = parseInt(lowTemp.value);
const customLowSpeed = parseInt(lowSpeed.value);
const customHighTemp = parseInt(highTemp.value);
const customHighSpeed = parseInt(highSpeed.value);
if (cpuTemp <= customLowTemp) {
newFanSpeed = customLowSpeed;
} else if (cpuTemp >= customHighTemp) {
newFanSpeed = customHighSpeed;
} else {
newFanSpeed = customLowSpeed +
((cpuTemp - customLowTemp) / (customHighTemp - customLowTemp)) *
(customHighSpeed - customLowSpeed);
}
}
// Apply max fan speed limit
newFanSpeed = Math.min(newFanSpeed, maxFanSpeed);
// Smooth fan speed changes
fanSpeed = fanSpeed * 0.7 + newFanSpeed * 0.3;
// Round to nearest integer
fanSpeed = Math.round(fanSpeed);
}
function updateFanCurve() {
const points = [];
if (fanCurveType === 'linear') {
points.push({x: 30, y: 0});
points.push({x: maxTemp, y: 100});
}
else if (fanCurveType === 'aggressive') {
points.push({x: 30, y: 0});
points.push({x: 50, y: 50});
points.push({x: maxTemp, y: 100});
}
else if (fanCurveType === 'silent') {
points.push({x: 30, y: 0});
points.push({x: 70, y: 40});
points.push({x: maxTemp, y: 100});
}
else if (fanCurveType === 'custom') {
const customLowTemp = parseInt(lowTemp.value);
const customLowSpeed = parseInt(lowSpeed.value);
const customHighTemp = parseInt(highTemp.value);
const customHighSpeed = parseInt(highSpeed.value);
points.push({x: 20, y: customLowSpeed});
points.push({x: customLowTemp, y: customLowSpeed});
points.push({x: customHighTemp, y: customHighSpeed});
points.push({x: 100, y: customHighSpeed});
}
fanCurvePoints = points;
updateChart();
}
function updateChart() {
fanCurveChart.data.datasets[0].data = fanCurvePoints;
fanCurveChart.update();
}
function updateUI() {
// Update CPU display
cpuTempElement.textContent = `${Math.round(cpuTemp)}°C`;
cpuLoadElement.textContent = `${cpuLoad}% Load`;
// Update temperature indicator position
const tempPercentage = Math.min(100, Math.max(0, cpuTemp));
tempIndicator.style.left = `${tempPercentage}%`;
// Update fan speed display
fanSpeedDisplay.textContent = `${fanSpeed}%`;
fanSpeedBar.style.width = `${fanSpeed}%`;
// Change fan speed bar color based on speed
if (fanSpeed < 30) {
fanSpeedBar.className = 'h-full bg-blue-500 rounded-full';
} else if (fanSpeed < 70) {
fanSpeedBar.className = 'h-full bg-yellow-500 rounded-full';
} else {
fanSpeedBar.className = 'h-full bg-red-500 rounded-full';
}
// Update fan animation
const animationDuration = fanSpeed > 0 ? `${2 - (fanSpeed / 100 * 1.8)}s` : '0s';
fanBlades.style.animationDuration = animationDuration;
// Change CPU color based on temperature
if (cpuTemp < 50) {
cpuTempElement.className = 'text-3xl font-bold text-blue-400';
} else if (cpuTemp < 70) {
cpuTempElement.className = 'text-3xl font-bold text-yellow-400';
} else if (cpuTemp < maxTemp) {
cpuTempElement.className = 'text-3xl font-bold text-orange-500';
} else {
cpuTempElement.className = 'text-3xl font-bold text-red-500';
}
}
function addLog(message) {
const now = new Date();
const timeString = now.toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.innerHTML = `<span class="text-gray-400">[${timeString}]</span> ${message}`;
systemLog.appendChild(logEntry);
systemLog.scrollTop = systemLog.scrollHeight;
// Keep only the last 50 log entries
if (systemLog.children.length > 50) {
systemLog.removeChild(systemLog.children[0]);
}
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=PrinzPesia/cpu-fan-simulator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>