Spaces:
Running
Running
Wichtig die Temperatur soll langsamer ansteigen bei CPU Auslastung. - Initial Deployment
dc02922
verified
<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> |