virtual-physics / index.html
AARIFSHABIR's picture
Make virtual physics app prepared by IITians in India - Initial Deployment
3e6997b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Virtual Physics Lab | Developed by IITians</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.experiment-area {
background-image: linear-gradient(to right, #f3f4f6, #e5e7eb);
box-shadow: inset 0 0 20px rgba(0,0,0,0.1);
position: relative;
overflow: hidden;
}
.ball {
position: absolute;
border-radius: 50%;
cursor: pointer;
transition: transform 0.1s;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.spring {
position: absolute;
background-color: #9ca3af;
border-radius: 5px;
}
.pendulum-string {
position: absolute;
background-color: #6b7280;
}
.pendulum-bob {
position: absolute;
border-radius: 50%;
background-color: #3b82f6;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
@keyframes pendulumSwing {
0% { transform: rotate(-30deg); }
50% { transform: rotate(30deg); }
100% { transform: rotate(-30deg); }
}
@keyframes gravityFall {
0% { top: 50px; }
100% { top: 350px; }
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<header class="bg-gradient-to-r from-blue-600 to-indigo-800 text-white py-4 px-6 shadow-lg">
<div class="container mx-auto flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-atom text-3xl"></i>
<h1 class="text-2xl font-bold">Virtual Physics Lab</h1>
</div>
<div class="hidden md:flex items-center space-x-4">
<span class="bg-yellow-400 text-gray-800 px-3 py-1 rounded-full text-sm font-bold">
Developed by IITians
</span>
<button class="bg-white text-indigo-700 px-4 py-2 rounded-full font-medium hover:bg-blue-50 transition">
Try Premium
</button>
</div>
<button class="md:hidden text-2xl">
<i class="fas fa-bars"></i>
</button>
</div>
</header>
<main class="container mx-auto px-4 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<div class="lg:w-1/4 bg-white rounded-xl shadow-md overflow-hidden">
<div class="bg-indigo-600 text-white px-4 py-3">
<h2 class="text-lg font-semibold flex items-center">
<i class="fas fa-flask mr-2"></i> Experiments
</h2>
</div>
<div class="p-4">
<ul class="space-y-2">
<li>
<button onclick="setupExperiment('projectile')" class="w-full text-left px-4 py-3 bg-blue-50 rounded-lg hover:bg-blue-100 transition flex items-center">
<i class="fas fa-basketball-ball mr-3 text-blue-600"></i>
Projectile Motion
</button>
</li>
<li>
<button onclick="setupExperiment('pendulum')" class="w-full text-left px-4 py-3 bg-blue-50 rounded-lg hover:bg-blue-100 transition flex items-center">
<i class="fas fa-clock mr-3 text-green-600"></i>
Simple Pendulum
</button>
</li>
<li>
<button onclick="setupExperiment('spring')" class="w-full text-left px-4 py-3 bg-blue-50 rounded-lg hover:bg-blue-100 transition flex items-center">
<i class="fas fa-expand mr-3 text-yellow-600"></i>
Spring Oscillator
</button>
</li>
<li>
<button onclick="setupExperiment('gravity')" class="w-full text-left px-4 py-3 bg-blue-50 rounded-lg hover:bg-blue-100 transition flex items-center">
<i class="fas fa-apple-alt mr-3 text-red-600"></i>
Free Fall (Gravity)
</button>
</li>
</ul>
</div>
<div class="border-t p-4 bg-gray-50">
<h3 class="font-medium mb-3 flex items-center">
<i class="fas fa-sliders-h mr-2"></i> Parameters
</h3>
<div id="parameters" class="space-y-4">
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Mass (kg)</label>
<input type="range" min="1" max="10" value="5" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" id="massSlider">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>1</span>
<span id="massValue">5</span>
<span>10</span>
</div>
</div>
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Angle (°)</label>
<input type="range" min="0" max="90" value="45" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" id="angleSlider">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>0</span>
<span id="angleValue">45</span>
<span>90</span>
</div>
</div>
<div class="pt-3">
<button onclick="startExperiment()" class="w-full bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-lg font-medium flex items-center justify-center">
<i class="fas fa-play mr-2"></i> Start Simulation
</button>
</div>
</div>
</div>
</div>
<div class="lg:w-3/4">
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="bg-indigo-600 text-white px-4 py-3 flex justify-between items-center">
<h2 class="text-lg font-semibold">Simulation Area</h2>
<div>
<button onclick="resetExperiment()" class="bg-white text-indigo-700 px-3 py-1 rounded text-sm font-medium hover:bg-blue-50 transition">
<i class="fas fa-redo mr-1"></i> Reset
</button>
</div>
</div>
<div class="experiment-area h-96 md:h-[500px] relative" id="experimentArea">
<!-- Experiment will be rendered here -->
<div class="absolute inset-0 flex items-center justify-center">
<div class="text-center text-gray-400">
<i class="fas fa-mouse-pointer text-4xl mb-2"></i>
<p>Select an experiment from the left panel to begin</p>
</div>
</div>
</div>
</div>
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-chart-line text-blue-500 mr-2"></i> Real-time Values
</h3>
<div class="space-y-2 text-sm">
<div class="flex justify-between">
<span>Velocity:</span>
<span id="velocityValue" class="font-medium">0 m/s</span>
</div>
<div class="flex justify-between">
<span>Acceleration:</span>
<span id="accelerationValue" class="font-medium">0 m/s²</span>
</div>
<div class="flex justify-between">
<span>Force:</span>
<span id="forceValue" class="font-medium">0 N</span>
</div>
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-info-circle text-green-500 mr-2"></i> Experiment Details
</h3>
<div id="experimentDesc" class="text-sm text-gray-600">
Select an experiment to see details
</div>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="font-medium text-gray-700 mb-2 flex items-center">
<i class="fas fa-book text-yellow-500 mr-2"></i> Key Concepts
</h3>
<ul id="keyConcepts" class="text-sm list-disc list-inside space-y-1 text-gray-600">
<li>Select an experiment first</li>
</ul>
</div>
</div>
</div>
</div>
</main>
<footer class="bg-gray-800 text-white py-8 px-6 mt-12">
<div class="container mx-auto">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<h3 class="text-xl font-bold flex items-center">
<i class="fas fa-atom mr-2"></i> Virtual Physics Lab
</h3>
<p class="text-gray-400 mt-1">Developed by IITians in India</p>
</div>
<div class="flex space-x-6">
<a href="#" class="hover:text-blue-300 transition">
<i class="fab fa-github text-2xl"></i>
</a>
<a href="#" class="hover:text-blue-400 transition">
<i class="fab fa-linkedin text-2xl"></i>
</a>
<a href="#" class="hover:text-red-400 transition">
<i class="fab fa-youtube text-2xl"></i>
</a>
</div>
</div>
<div class="border-t border-gray-700 mt-6 pt-6 text-center text-gray-400 text-sm">
<p>© 2023 Virtual Physics Lab. All rights reserved. | Designed as an educational tool by IIT graduates.</p>
</div>
</div>
</footer>
<script>
// Current experiment type
let currentExperiment = null;
let animationId = null;
let pendulumAngle = 30;
let pendulumDirection = 1;
let springPosition = 200;
let springDirection = 1;
let gravityPosition = 50;
let projectileX = 50;
let projectileY = 300;
let projectileVelocityX = 5;
let projectileVelocityY = -5;
let projectileGravity = 0.2;
// DOM elements
const experimentArea = document.getElementById('experimentArea');
const massSlider = document.getElementById('massSlider');
const massValue = document.getElementById('massValue');
const angleSlider = document.getElementById('angleSlider');
const angleValue = document.getElementById('angleValue');
const velocityValue = document.getElementById('velocityValue');
const accelerationValue = document.getElementById('accelerationValue');
const forceValue = document.getElementById('forceValue');
const experimentDesc = document.getElementById('experimentDesc');
const keyConcepts = document.getElementById('keyConcepts');
// Update slider values
massSlider.addEventListener('input', () => {
massValue.textContent = massSlider.value;
});
angleSlider.addEventListener('input', () => {
angleValue.textContent = angleSlider.value;
});
// Setup experiment
function setupExperiment(type) {
currentExperiment = type;
experimentArea.innerHTML = '';
// Clear any existing animations
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
// Reset positions
pendulumAngle = 30;
springPosition = 200;
gravityPosition = 50;
projectileX = 50;
projectileY = 300;
projectileVelocityX = 5;
projectileVelocityY = -5;
// Setup based on experiment type
switch(type) {
case 'projectile':
setupProjectile();
experimentDesc.innerHTML = `
<p>Simulate the motion of a projectile under gravity, demonstrating how objects follow a parabolic trajectory when launched at an angle.</p>
`;
keyConcepts.innerHTML = `
<li>Parabolic trajectory</li>
<li>Horizontal motion is uniform</li>
<li>Vertical motion is accelerated</li>
<li>Independent x and y components</li>
`;
break;
case 'pendulum':
setupPendulum();
experimentDesc.innerHTML = `
<p>Explore simple harmonic motion with a pendulum, observing the relationship between length, gravity, and period of oscillation.</p>
`;
keyConcepts.innerHTML = `
<li>Simple harmonic motion</li>
<li>Period depends on length</li>
<li>Restoring force proportional to displacement</li>
<li>Energy conservation</li>
`;
break;
case 'spring':
setupSpring();
experimentDesc.innerHTML = `
<p>Visualize Hooke's Law in action with a spring-mass system, showing how the restoring force relates to displacement.</p>
`;
keyConcepts.innerHTML = `
<li>Hooke's Law (F = -kx)</li>
<li>Simple harmonic motion</li>
<li>Period depends on mass</li>
<li>Energy conservation</li>
`;
break;
case 'gravity':
setupGravity();
experimentDesc.innerHTML = `
<p>Demonstrate free fall under gravity, showing how all objects accelerate at the same rate regardless of mass in a vacuum.</p>
`;
keyConcepts.innerHTML = `
<li>Uniform acceleration (g = 9.8 m/s²)</li>
<li>Independent of mass</li>
<li>Potential to kinetic energy conversion</li>
<li>Equations of motion</li>
`;
break;
}
}
// Setup projectile experiment
function setupProjectile() {
// Add ground
const ground = document.createElement('div');
ground.className = 'absolute bottom-0 left-0 right-0 h-2 bg-green-700';
experimentArea.appendChild(ground);
// Add ball
const ball = document.createElement('div');
ball.id = 'projectileBall';
ball.className = 'ball bg-red-500 w-8 h-8';
ball.style.left = '50px';
ball.style.top = '300px';
experimentArea.appendChild(ball);
// Update initial velocity based on angle
projectileVelocityX = 5 * Math.cos(angleSlider.value * Math.PI / 180);
projectileVelocityY = -5 * Math.sin(angleSlider.value * Math.PI / 180);
}
// Setup pendulum experiment
function setupPendulum() {
// Add ceiling
const ceiling = document.createElement('div');
ceiling.className = 'absolute top-0 left-0 right-0 h-2 bg-blue-800';
experimentArea.appendChild(ceiling);
// Add pivot point
const pivot = document.createElement('div');
pivot.className = 'absolute bg-black rounded-full w-5 h-5';
pivot.style.left = 'calc(50% - 10px)';
pivot.style.top = '10px';
experimentArea.appendChild(pivot);
// Add string
const string = document.createElement('div');
string.id = 'pendulumString';
string.className = 'pendulum-string w-1 h-60';
string.style.left = '50%';
string.style.top = '20px';
string.style.transformOrigin = 'top center';
string.style.transform = `rotate(${pendulumAngle}deg)`;
experimentArea.appendChild(string);
// Add bob
const bob = document.createElement('div');
bob.id = 'pendulumBob';
bob.className = 'pendulum-bob w-12 h-12';
bob.style.left = 'calc(50% - 24px)';
bob.style.top = '250px';
experimentArea.appendChild(bob);
}
// Setup spring experiment
function setupSpring() {
// Add ceiling
const ceiling = document.createElement('div');
ceiling.className = 'absolute top-0 left-0 right-0 h-2 bg-blue-800';
experimentArea.appendChild(ceiling);
// Add spring
const spring = document.createElement('div');
spring.id = 'spring';
spring.className = 'spring w-1 h-40';
spring.style.left = 'calc(50% - 1px)';
spring.style.top = '10px';
experimentArea.appendChild(spring);
// Add mass
const mass = document.createElement('div');
mass.id = 'springMass';
mass.className = 'absolute bg-indigo-600 w-16 h-8 rounded-md';
mass.style.left = 'calc(50% - 32px)';
mass.style.top = '200px';
experimentArea.appendChild(mass);
}
// Setup gravity experiment
function setupGravity() {
// Add ground
const ground = document.createElement('div');
ground.className = 'absolute bottom-0 left-0 right-0 h-2 bg-green-700';
experimentArea.appendChild(ground);
// Add ball
const ball = document.createElement('div');
ball.id = 'gravityBall';
ball.className = 'ball bg-purple-500 w-10 h-10';
ball.style.left = 'calc(50% - 20px)';
ball.style.top = '50px';
experimentArea.appendChild(ball);
}
// Start experiment
function startExperiment() {
if (!currentExperiment) return;
// Update parameters based on sliders
const mass = parseFloat(massSlider.value);
const angle = parseFloat(angleSlider.value);
// Reset experiment first
setupExperiment(currentExperiment);
// Start appropriate animation
switch(currentExperiment) {
case 'projectile':
animateProjectile(mass, angle);
break;
case 'pendulum':
animatePendulum(mass);
break;
case 'spring':
animateSpring(mass);
break;
case 'gravity':
animateGravity(mass);
break;
}
}
// Animate projectile
function animateProjectile(mass, angle) {
const ball = document.getElementById('projectileBall');
projectileVelocityX = 5 * mass * Math.cos(angle * Math.PI / 180);
projectileVelocityY = -5 * mass * Math.sin(angle * Math.PI / 180);
function update() {
// Update velocity with gravity
projectileVelocityY += projectileGravity;
// Update position
projectileX += projectileVelocityX;
projectileY += projectileVelocityY;
// Bounce off bottom
if (projectileY > experimentArea.offsetHeight - 30) {
projectileY = experimentArea.offsetHeight - 30;
projectileVelocityY *= -0.7; // Coefficient of restitution
projectileVelocityX *= 0.9; // Friction
}
// Update ball position
ball.style.left = `${projectileX}px`;
ball.style.top = `${projectileY}px`;
// Update stats
const velocity = Math.sqrt(projectileVelocityX * projectileVelocityX + projectileVelocityY * projectileVelocityY);
velocityValue.textContent = `${velocity.toFixed(2)} m/s`;
accelerationValue.textContent = `Y: ${projectileGravity.toFixed(2)} m/s², X: 0 m/s²`;
forceValue.textContent = `${(mass * projectileGravity).toFixed(2)} N (Weight)`;
// Stop if ball stops moving
if (Math.abs(projectileVelocityX) < 0.1 && Math.abs(projectileVelocityY) < 0.5 && projectileY > experimentArea.offsetHeight - 30) {
return;
}
animationId = requestAnimationFrame(update);
}
update();
}
// Animate pendulum
function animatePendulum(mass) {
const string = document.getElementById('pendulumString');
const bob = document.getElementById('pendulumBob');
const length = 250; // Pendulum length in pixels
const damping = 0.995; // Damping factor
const gravity = 0.1 * mass;
function update() {
// Calculate angular acceleration
const acceleration = -gravity * Math.sin(pendulumAngle * Math.PI / 180);
// Update angular velocity
pendulumDirection += acceleration;
// Update angle
pendulumAngle += pendulumDirection;
// Apply damping
pendulumDirection *= damping;
// Update pendulum position
string.style.transform = `rotate(${pendulumAngle}deg)`;
// Calculate bob position
const bobX = experimentArea.offsetWidth / 2 + length * Math.sin(pendulumAngle * Math.PI / 180);
const bobY = 20 + length * Math.cos(pendulumAngle * Math.PI / 180);
bob.style.left = `${bobX - 24}px`;
bob.style.top = `${bobY}px`;
// Update stats
const velocity = Math.abs(pendulumDirection * length) / 10;
velocityValue.textContent = `${velocity.toFixed(2)} m/s`;
const angularAcceleration = Math.abs(acceleration);
accelerationValue.textContent = `${angularAcceleration.toFixed(2)} rad/s²`;
const force = mass * gravity * Math.sin(pendulumAngle * Math.PI / 180);
forceValue.textContent = `${force.toFixed(2)} N (Restoring force)`;
animationId = requestAnimationFrame(update);
}
update();
}
// Animate spring
function animateSpring(mass) {
const spring = document.getElementById('spring');
const massElement = document.getElementById('springMass');
const springConstant = 0.5; // k in Hooke's Law
const damping = 0.98; // Damping factor
let velocity = 0;
function update() {
// Calculate displacement from equilibrium
const equilibrium = 300 - 30 * mass;
const displacement = springPosition - equilibrium;
// Calculate acceleration (Hooke's Law: F = -kx)
const acceleration = -springConstant * displacement / mass;
// Update velocity
velocity += acceleration;
velocity *= damping;
// Update position
springPosition += velocity;
// Update spring and mass position
spring.style.height = `${springPosition}px`;
massElement.style.top = `${springPosition - 8}px`;
// Update stats
velocityValue.textContent = `${Math.abs(velocity).toFixed(2)} m/s`;
accelerationValue.textContent = `${Math.abs(acceleration).toFixed(2)} m/s²`;
forceValue.textContent = `${Math.abs(springConstant * displacement).toFixed(2)} N (Spring force)`;
animationId = requestAnimationFrame(update);
}
update();
}
// Animate gravity
function animateGravity(mass) {
const ball = document.getElementById('gravityBall');
let velocity = 0;
function update() {
// Apply gravity
const gravity = 0.5 * mass;
velocity += gravity;
// Update position
gravityPosition += velocity;
// Stop at ground
if (gravityPosition > experimentArea.offsetHeight - 30) {
gravityPosition = experimentArea.offsetHeight - 30;
velocity *= -0.5; // Bounce with coefficient of restitution
}
// Update ball position
ball.style.top = `${gravityPosition}px`;
// Update stats
velocityValue.textContent = `${velocity.toFixed(2)} m/s`;
accelerationValue.textContent = `${gravity.toFixed(2)} m/s²`;
forceValue.textContent = `${(mass * gravity).toFixed(2)} N (Weight)`;
animationId = requestAnimationFrame(update);
}
update();
}
// Reset experiment
function resetExperiment() {
if (currentExperiment) {
setupExperiment(currentExperiment);
}
// Reset stats
velocityValue.textContent = '0 m/s';
accelerationValue.textContent = '0 m/s²';
forceValue.textContent = '0 N';
}
</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=AARIFSHABIR/virtual-physics" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>