You may need to reload this page for the Simulator to fully load.

<div id="planetary-instability-model" class="bg-gray-900 text-gray-200 flex flex-col items-center justify-center min-h-screen p-4">
   
   <link rel="preconnect" href="https://fonts.googleapis.com" />
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
   <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Fira+Code&display=swap" rel="stylesheet" />
   <style>
       body { font-family: 'Inter', sans-serif; }
       .fira-code { font-family: 'Fira Code', monospace; }
       .slider-label { display: flex; justify-content: space-between; font-size: 0.875rem; }
       #data-readout span:nth-child(odd) { color: #9ca3af; }
   </style>

   <div class="flex flex-col items-center">
       
       <div id="simulation-container" class="w-[1000px] h-[1000px] bg-black rounded-t-lg shadow-2xl overflow-hidden relative border-2 border-gray-700"></div>

       
       <div id="ui-container" class="w-[1000px] h-[300px] bg-gray-800/50 backdrop-blur-sm rounded-b-lg shadow-2xl p-6 grid grid-cols-3 gap-6 border-x-2 border-b-2 border-gray-700">
           
           <div class="space-y-3">
               <h2 class="text-lg font-bold text-white border-b border-gray-600 pb-1">Simulation Controls</h2>
                <div>
                   <div class="slider-label"><span>Time Scale</span><span id="timeScale-val" class="fira-code"></span></div>
                   <input id="timeScale" type="range" min="1" max="500000" value="250000" class="w-full" />
                </div>
               <div>
                   <div class="slider-label"><span>Melt Rate (Gt/yr)</span><span id="meltRate-val" class="fira-code"></span></div>
                   <input id="meltRate" type="range" min="0" max="500" value="400" class="w-full" />
                </div>
                <div>
                   <label for="viscosity" class="block text-sm">Viscosity (Outer Core)</label>
                   <input id="viscosity" type="number" value="4.2146445" step="0.000001" class="bg-gray-700 rounded p-1 w-full fira-code" />
               </div>
               <div>
                   <div class="slider-label"><span>Torque Scale</span><span id="torqueMagnitudeScale-val" class="fira-code"></span></div>
                   <input id="torqueMagnitudeScale" type="range" min="0" max="0.01" value="0.001" step="0.00001" class="w-full" />
               </div>
                <div class="flex items-center gap-4 pt-2">
                   <button id="start-button" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-full">Start</button>
                   <button id="reset-button" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded w-full">Reset</button>
               </div>
           </div>

           <div class="space-y-1 bg-gray-900/50 p-3 rounded-lg border border-gray-700 fira-code text-xs">
                <h2 class="text-lg font-bold text-white border-b border-gray-600 pb-1 mb-2 font-sans">Live Data</h2>
                <div id="data-readout" class="grid grid-cols-2 gap-x-4">
                   <span>Liquid Ratio:</span><span id="liquidMassRatio-data"></span>
                   <span>Eff. Core Mass:</span><span id="effectiveCoreMass-data"></span>
                   <span>Surface Water:</span><span id="surfaceWaterMass-data"></span>
                   <span>Remaining Ice:</span><span id="remainingIceMass-data"></span>
                   <span>Angular Vel:</span><span id="angularVelocity-data"></span>
                   <span>Applied Torque:</span><span id="torqueMagnitude-data"></span>
                   <span>LOD Shift (Up):</span><span id="objectUpDeviation-data"></span>
                   <span>COM Shift (Rot Vec):</span><span id="rotationVectorDeviation-data"></span>
                   <span>Simulated Days:</span><span id="simulatedTime-data"></span>
                </div>
           </div>
           
           <div class="space-y-3">
                <h2 class="text-lg font-bold text-white border-b border-gray-600 pb-1">Initial Conditions (Gt)</h2>
                <div>
                   <label class="block text-sm">Total Mass</label>
                   <input id="totalEarthMass" type="number" value="5972000000" class="bg-gray-700 rounded p-1 w-full" />
                </div>
                <div>
                   <label class="block text-sm">Outer Core Mass</label>
                   <input id="outerCoreMass" type="number" value="1900000000" class="bg-gray-700 rounded p-1 w-full" />
                </div>
                <div>
                   <label class="block text-sm">Total Ice Mass</label>
                   <input id="totalIceMass" type="number" value="24000000" class="bg-gray-700 rounded p-1 w-full" />
                </div>
           </div>
       </div>
   </div>
   <script src="https://cdn.tailwindcss.com"></script>
   <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
   <script src="https://cdn.jsdelivr.net/npm/cannon@0.6.2/build/cannon.min.js"></script>
   <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
   <script>
       (function() {
           let scene, camera, renderer, controls, world, clock;
           let sphereMesh, trenchMesh, sphereBody, trenchBody, topIce, bottomIce;
           let remainingIceMass, surfaceWaterMass, effectiveCoreMass, effectiveLiquidMass, liquidMassRatio, simulationTimeDays = 0;
           let animationFrameId;
           let timeScale, meltRate, viscosity, torqueMagnitudeScale;
           let totalEarthMass, outerCoreMass, totalIceMass;
           const initialSurfaceWaterMass = 1385504; // 0.0232% of total mass
           const gravitationalAcceleration = 9.81, centerOffset = new THREE.Vector3(0.1, 0, 0);

           // UI elements from Trench_Simulator5.html
           const simContainer = document.getElementById('simulation-container');
           const sliders = { timeScale: document.getElementById('timeScale'), meltRate: document.getElementById('meltRate'), viscosity: document.getElementById('viscosity'), torqueMagnitudeScale: document.getElementById('torqueMagnitudeScale') };
           const sliderVals = { timeScale: document.getElementById('timeScale-val'), meltRate: document.getElementById('meltRate-val'), torqueMagnitudeScale: document.getElementById('torqueMagnitudeScale-val') };
           const dataReadouts = { liquidMassRatio: document.getElementById('liquidMassRatio-data'), effectiveCoreMass: document.getElementById('effectiveCoreMass-data'), surfaceWaterMass: document.getElementById('surfaceWaterMass-data'), remainingIceMass: document.getElementById('remainingIceMass-data'), angularVelocity: document.getElementById('angularVelocity-data'), torqueMagnitude: document.getElementById('torqueMagnitude-data'), objectUpDeviation: document.getElementById('objectUpDeviation-data'), rotationVectorDeviation: document.getElementById('rotationVectorDeviation-data'), simulatedTime: document.getElementById('simulatedTime-data') };
           const initialInputs = { totalEarthMass: document.getElementById('totalEarthMass'), outerCoreMass: document.getElementById('outerCoreMass'), totalIceMass: document.getElementById('totalIceMass') };
           const startButton = document.getElementById('start-button');
           const resetButton = document.getElementById('reset-button');
           
           let isSimulationRunning = false;

           // Constants for a precise, deterministic motion as requested
           const SECONDS_PER_DAY = 24 * 3600;
           const SECONDS_PER_YEAR = 365.25 * SECONDS_PER_DAY;
           const ORBIT_RADIUS = 10000;
           const AXIAL_ANGULAR_VELOCITY = (2 * Math.PI) / SECONDS_PER_DAY;
           const ORBITAL_ANGULAR_VELOCITY = (2 * Math.PI) / SECONDS_PER_YEAR;

           init();
           setupUI();
           resetSimulation();
           
           function init() {
               // Three.js Scene and Camera setup from ChatGPTPIM.html
               scene = new THREE.Scene();
               scene.background = new THREE.Color(0x000000);
               camera = new THREE.PerspectiveCamera(75, 1000 / 1000, 0.1, 50000);
               // Adjusted camera position to better frame the sphere inside the toroid
               camera.position.set(0, 0, 15000);
               renderer = new THREE.WebGLRenderer({ antialias: true });
               renderer.setSize(1000, 1000);
               simContainer.appendChild(renderer.domElement);
               
               const light = new THREE.DirectionalLight(0xffffff, 1);
               light.position.set(100, 300, 200);
               scene.add(new THREE.AmbientLight(0x404040));
               scene.add(light);
               controls = new THREE.OrbitControls(camera, renderer.domElement);
               controls.enableDamping = true;

               // Physics world is now used for all motion
               world = new CANNON.World();
               world.gravity.set(0, -gravitationalAcceleration, 0);

               // Sphere from ChatGPTP.html
               const sphereGeo = new THREE.SphereGeometry(10, 32, 32);
               const sphereMat = new THREE.MeshPhongMaterial({ color: 0x446688 });
               sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
               scene.add(sphereMesh);

               const sphereShape = new CANNON.Sphere(10);
               sphereBody = new CANNON.Body({ mass: 1, shape: sphereShape });
               world.addBody(sphereBody);
               
               // Trench from ChatGPTP.html
               const trenchGeo = new THREE.TorusGeometry(10000, 40, 32, 64);
               const trenchMat = new THREE.MeshPhongMaterial({ color: 0x333344, side: THREE.DoubleSide, transparent: true, opacity: 0.3 });
               trenchMesh = new THREE.Mesh(trenchGeo, trenchMat);
               trenchMesh.rotation.x = Math.PI / 2;
               scene.add(trenchMesh);

               const vertices = trenchGeo.attributes.position.array;
               const indices = trenchGeo.index.array;
               const cannonTrimesh = new CANNON.Trimesh(vertices, indices);

               trenchBody = new CANNON.Body({ mass: 0 });
               trenchBody.addShape(cannonTrimesh);
               trenchBody.quaternion.setFromEuler(Math.PI / 2, 0, 0);
               world.addBody(trenchBody);

               // Visual ice caps from c_script_Fuid_Inertia.txt
               const iceGeo = new THREE.SphereGeometry(10, 16, 16); // Increased radius for visibility
               const iceMat = new THREE.MeshPhongMaterial({ color: 0xffffff });
               topIce = new THREE.Mesh(iceGeo, iceMat);
               bottomIce = new THREE.Mesh(iceGeo, iceMat);
               
               // Parent the ice caps to the sphere mesh
               sphereMesh.add(topIce);
               sphereMesh.add(bottomIce);
           }

           function render() {
               controls.update();
               renderer.render(scene, camera);
           }

           function setupUI() {
               sliders.timeScale.addEventListener('input', (e) => { timeScale = parseFloat(e.target.value); sliderVals.timeScale.textContent = e.target.value; });
               sliders.meltRate.addEventListener('input', (e) => { meltRate = parseFloat(e.target.value); sliderVals.meltRate.textContent = e.target.value; });
               sliders.viscosity.addEventListener('input', (e) => { viscosity = parseFloat(e.target.value); });
               sliders.torqueMagnitudeScale.addEventListener('input', (e) => { torqueMagnitudeScale = parseFloat(e.target.value); sliderVals.torqueMagnitudeScale.textContent = torqueMagnitudeScale.toExponential(2); });
               sliderVals.timeScale.textContent = sliders.timeScale.value;
               sliderVals.meltRate.textContent = sliders.meltRate.value;
               sliderVals.torqueMagnitudeScale.textContent = parseFloat(sliders.torqueMagnitudeScale.value).toExponential(2);
               startButton.addEventListener('click', startSimulation);
               resetButton.addEventListener('click', resetSimulation);
           }

           function resetSimulation() {
               if (animationFrameId) {
                   cancelAnimationFrame(animationFrameId);
                   animationFrameId = null;
               }
               isSimulationRunning = false;
               clock = new THREE.Clock();
               
               // Ensure all initial values are parsed to floats
               totalEarthMass = parseFloat(initialInputs.totalEarthMass.value);
               outerCoreMass = parseFloat(initialInputs.outerCoreMass.value);
               totalIceMass = parseFloat(initialInputs.totalIceMass.value);
               viscosity = parseFloat(sliders.viscosity.value);
               timeScale = parseFloat(sliders.timeScale.value);
               meltRate = parseFloat(sliders.meltRate.value);
               torqueMagnitudeScale = parseFloat(sliders.torqueMagnitudeScale.value);
               
               remainingIceMass = totalIceMass;
               surfaceWaterMass = initialSurfaceWaterMass;
               simulationTimeDays = 0;
               
               // Reset position of physics body
               sphereBody.position.set(ORBIT_RADIUS, 5, 0);
               sphereBody.velocity.set(0, 0, 0);
               sphereBody.angularVelocity.set(0, AXIAL_ANGULAR_VELOCITY, 0);
               sphereBody.quaternion.setFromAxisAngle(new CANNON.Vec3(0, 1, 0), 0);
               sphereBody.mass = 1;

               updateCalculations();
               updateAllReadouts();
               updateIceCapVisuals();
               render();
           }
           
           function startSimulation() {
               if (isSimulationRunning) return;
               isSimulationRunning = true;
               animate();
           }

           function animate() {
               animationFrameId = requestAnimationFrame(animate);
               
               const deltaTime = clock.getDelta();
               const scaledDeltaTime = deltaTime * timeScale;

               // Step the physics world to apply torque and update angular velocity
               world.step(1 / 60, deltaTime, 3);
               
               // Update sphere's orbital movement using the correct units
               const orbitalAngle = (simulationTimeDays / 365.25) * 2 * Math.PI;
               sphereBody.position.x = ORBIT_RADIUS * Math.cos(orbitalAngle);
               sphereBody.position.z = ORBIT_RADIUS * Math.sin(orbitalAngle);
               
               // Update visuals based on physics body
               sphereMesh.position.copy(sphereBody.position);
               sphereMesh.quaternion.copy(sphereBody.quaternion);

               // Increment simulated time based on the time scale slider
               simulationTimeDays += scaledDeltaTime / SECONDS_PER_DAY;
               
               // Apply empirical physics (sinusoidal melt and torque)
               applyEmpiricalPhysics(scaledDeltaTime);
               
               controls.update();
               renderer.render(scene, camera);
               updateAllReadouts(scaledDeltaTime);
           }
           
           function applyEmpiricalPhysics(effectiveDeltaTime) {
               const secondsPerYear = 365.25 * 24 * 3600;
               const meltFactor = (Math.sin(simulationTimeDays * 0.01) + 1) / 2;
               const meltPerFrame = meltRate * meltFactor * effectiveDeltaTime / secondsPerYear;
               
               if (remainingIceMass > 0) {
                   const melt = Math.min(meltPerFrame, remainingIceMass);
                   remainingIceMass -= melt;
                   surfaceWaterMass += melt;
                   updateIceCapVisuals();
               }
               updateCalculations();
               
               const threeQuaternion = new THREE.Quaternion(sphereBody.quaternion.x, sphereBody.quaternion.y, sphereBody.quaternion.z, sphereBody.quaternion.w);
               const torqueArmWorld = centerOffset.clone().applyQuaternion(threeQuaternion);
               const gravitationalForceVector = new THREE.Vector3(0, -gravitationalAcceleration, 0).multiplyScalar(sphereBody.mass);
               const torqueVector = new THREE.Vector3().crossVectors(torqueArmWorld, gravitationalForceVector);
               torqueVector.multiplyScalar((liquidMassRatio / 100) * torqueMagnitudeScale);
               const cannonTorque = new CANNON.Vec3(torqueVector.x, torqueVector.y, torqueVector.z);
               sphereBody.torque.vadd(cannonTorque, sphereBody.torque);
           }

           function updateCalculations() {
               effectiveCoreMass = outerCoreMass / viscosity;
               effectiveLiquidMass = effectiveCoreMass + surfaceWaterMass;
               liquidMassRatio = (effectiveLiquidMass / totalEarthMass) * 100;
           }

           function updateIceCapVisuals() {
               const ratio = (totalIceMass > 0) ? remainingIceMass / totalIceMass : 0;
               const scale = 0.8 * Math.sqrt(ratio); // Adjusted scale for better visibility
               const visibleScale = Math.max(0.001, scale);
               topIce.scale.set(visibleScale, visibleScale, visibleScale);
               bottomIce.scale.set(visibleScale, visibleScale, visibleScale);
               topIce.position.y = 5 * (1 + (1-ratio));
               bottomIce.position.y = -5 * (1 + (1-ratio));
           }

           function updateAllReadouts(effectiveDeltaTime = 0) {
               const angVel = sphereBody.angularVelocity.length();
               
               const sphereUp = new THREE.Vector3(0, 1, 0).applyQuaternion(sphereMesh.quaternion);
               const objectUpDeviationDegrees = sphereUp.angleTo(new THREE.Vector3(0, 1, 0)) * (180 / Math.PI);
               
               let rotationVectorDeviationDegrees = 0;
               if (sphereBody.angularVelocity.lengthSquared() > 1e-6) {
                   const av = sphereBody.angularVelocity.unit();
                   rotationVectorDeviationDegrees = new THREE.Vector3(av.x, av.y, av.z).angleTo(new THREE.Vector3(0, 1, 0)) * (180 / Math.PI);
               }
               
               const threeQuaternion = new THREE.Quaternion(sphereBody.quaternion.x, sphereBody.quaternion.y, sphereBody.quaternion.z, sphereBody.quaternion.w);
               const torqueArmWorld = centerOffset.clone().applyQuaternion(threeQuaternion);
               const gravitationalForceVector = new THREE.Vector3(0, -gravitationalAcceleration, 0).multiplyScalar(sphereBody.mass);
               const torqueVector = new THREE.Vector3().crossVectors(torqueArmWorld, gravitationalForceVector);
               torqueVector.multiplyScalar((liquidMassRatio / 100) * torqueMagnitudeScale);

               dataReadouts.liquidMassRatio.textContent = `${liquidMassRatio.toFixed(4)}%`;
               dataReadouts.effectiveCoreMass.textContent = `${effectiveCoreMass.toFixed(4)} Gt`;
               dataReadouts.surfaceWaterMass.textContent = `${surfaceWaterMass.toFixed(4)} Gt`;
               dataReadouts.remainingIceMass.textContent = `${remainingIceMass.toFixed(4)} Gt`;
               dataReadouts.angularVelocity.textContent = `${angVel.toFixed(4)} rad/s`;
               dataReadouts.torqueMagnitude.textContent = torqueVector.length().toExponential(4);
               dataReadouts.objectUpDeviation.textContent = `${objectUpDeviationDegrees.toFixed(2)}°`;
               dataReadouts.rotationVectorDeviation.textContent = `${rotationVectorDeviationDegrees.toFixed(2)}°`;
               dataReadouts.simulatedTime.textContent = `${simulationTimeDays.toFixed(2)}`;
           }
       })();
   </script>
</div>
 

We need your consent to load the translations

We use a third-party service to translate the website content that may collect data about your activity. Please review the details in the privacy policy and accept the service to view the translations.