| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Embodied AI - Autonomous Vehicle Simulator</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| color: white; |
| } |
| |
| .header { |
| background: rgba(0, 0, 0, 0.3); |
| padding: 20px; |
| text-align: center; |
| backdrop-filter: blur(10px); |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
| } |
| |
| .header h1 { |
| font-size: 2.5rem; |
| margin-bottom: 10px; |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); |
| } |
| |
| .header p { |
| font-size: 1.1rem; |
| opacity: 0.9; |
| } |
| |
| .main-container { |
| display: flex; |
| flex: 1; |
| gap: 20px; |
| padding: 20px; |
| max-width: 1400px; |
| margin: 0 auto; |
| width: 100%; |
| } |
| |
| .simulation-area { |
| flex: 1; |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 15px; |
| padding: 20px; |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.2); |
| } |
| |
| .canvas-container { |
| position: relative; |
| width: 100%; |
| height: 600px; |
| background: #1a1a2e; |
| border-radius: 10px; |
| overflow: hidden; |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); |
| } |
| |
| #simulationCanvas { |
| width: 100%; |
| height: 100%; |
| display: block; |
| } |
| |
| .controls-panel { |
| width: 300px; |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 15px; |
| padding: 20px; |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.2); |
| height: fit-content; |
| } |
| |
| .control-section { |
| margin-bottom: 25px; |
| } |
| |
| .control-section h3 { |
| font-size: 1.2rem; |
| margin-bottom: 15px; |
| color: #fff; |
| border-bottom: 2px solid rgba(255, 255, 255, 0.3); |
| padding-bottom: 8px; |
| } |
| |
| .control-group { |
| margin-bottom: 15px; |
| } |
| |
| .control-group label { |
| display: block; |
| margin-bottom: 5px; |
| font-weight: 500; |
| color: #e0e0e0; |
| } |
| |
| .control-group input, .control-group select { |
| width: 100%; |
| padding: 8px 12px; |
| border: none; |
| border-radius: 8px; |
| background: rgba(255, 255, 255, 0.2); |
| color: white; |
| font-size: 14px; |
| } |
| |
| .control-group input::placeholder { |
| color: rgba(255, 255, 255, 0.6); |
| } |
| |
| .btn { |
| width: 100%; |
| padding: 12px; |
| border: none; |
| border-radius: 8px; |
| font-size: 16px; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| margin-bottom: 10px; |
| } |
| |
| .btn-primary { |
| background: linear-gradient(45deg, #4CAF50, #45a049); |
| color: white; |
| } |
| |
| .btn-secondary { |
| background: linear-gradient(45deg, #2196F3, #1976D2); |
| color: white; |
| } |
| |
| .btn-danger { |
| background: linear-gradient(45deg, #f44336, #d32f2f); |
| color: white; |
| } |
| |
| .btn:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); |
| } |
| |
| .stats { |
| background: rgba(0, 0, 0, 0.3); |
| padding: 15px; |
| border-radius: 8px; |
| margin-top: 15px; |
| } |
| |
| .stats h4 { |
| margin-bottom: 10px; |
| color: #fff; |
| } |
| |
| .stat-item { |
| display: flex; |
| justify-content: space-between; |
| margin-bottom: 8px; |
| font-size: 14px; |
| } |
| |
| .stat-label { |
| color: #ccc; |
| } |
| |
| .stat-value { |
| color: #4CAF50; |
| font-weight: 600; |
| } |
| |
| .ai-info { |
| background: rgba(0, 0, 0, 0.3); |
| padding: 15px; |
| border-radius: 8px; |
| margin-top: 15px; |
| font-size: 13px; |
| line-height: 1.4; |
| } |
| |
| .legend { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 15px; |
| margin-top: 15px; |
| } |
| |
| .legend-item { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| font-size: 12px; |
| } |
| |
| .legend-color { |
| width: 16px; |
| height: 16px; |
| border-radius: 50%; |
| } |
| |
| @media (max-width: 768px) { |
| .main-container { |
| flex-direction: column; |
| } |
| |
| .controls-panel { |
| width: 100%; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="header"> |
| <h1>🚗 Embodied AI Vehicle Simulator</h1> |
| <p>Interactive autonomous vehicle navigation with real-time AI decision making</p> |
| </div> |
|
|
| <div class="main-container"> |
| <div class="simulation-area"> |
| <div class="canvas-container"> |
| <canvas id="simulationCanvas"></canvas> |
| </div> |
| |
| <div class="legend"> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #4CAF50;"></div> |
| <span>Autonomous Vehicle</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #f44336;"></div> |
| <span>Obstacles</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #2196F3;"></div> |
| <span>Target Destinations</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background: #FFC107;"></div> |
| <span>Sensors (LiDAR)</span> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="controls-panel"> |
| <div class="control-section"> |
| <h3>🎮 Simulation Controls</h3> |
| <button class="btn btn-primary" id="startBtn">Start Simulation</button> |
| <button class="btn btn-secondary" id="pauseBtn">Pause</button> |
| <button class="btn btn-danger" id="resetBtn">Reset</button> |
| </div> |
|
|
| <div class="control-section"> |
| <h3>⚙️ Parameters</h3> |
| <div class="control-group"> |
| <label>Vehicle Speed</label> |
| <input type="range" id="speedSlider" min="0" max="5" value="2" step="0.5"> |
| <span id="speedValue">2.0</span> |
| </div> |
| <div class="control-group"> |
| <label>Sensor Range</label> |
| <input type="range" id="sensorRange" min="50" max="200" value="100" step="10"> |
| <span id="sensorValue">100</span> |
| </div> |
| <div class="control-group"> |
| <label>AI Behavior</label> |
| <select id="behaviorSelect"> |
| <option value="cautious">Cautious</option> |
| <option value="normal" selected>Normal</option> |
| <option value="aggressive">Aggressive</option> |
| </select> |
| </div> |
| </div> |
|
|
| <div class="control-section"> |
| <h3>🏭 Environment</h3> |
| <button class="btn btn-secondary" id="addObstacleBtn">Add Obstacle</button> |
| <button class="btn btn-secondary" id="addTargetBtn">Add Target</button> |
| <button class="btn btn-danger" id="clearBtn">Clear All</button> |
| </div> |
|
|
| <div class="stats"> |
| <h4>📊 Real-time Stats</h4> |
| <div class="stat-item"> |
| <span class="stat-label">Distance Traveled:</span> |
| <span class="stat-value" id="distanceTraveled">0m</span> |
| </div> |
| <div class="stat-item"> |
| <span class="stat-label">Targets Reached:</span> |
| <span class="stat-value" id="targetsReached">0</span> |
| </div> |
| <div class="stat-item"> |
| <span class="stat-label">Obstacles Avoided:</span> |
| <span class="stat-value" id="obstaclesAvoided">0</span> |
| </div> |
| <div class="stat-item"> |
| <span class="stat-label">AI Decisions/sec:</span> |
| <span class="stat-value" id="decisionsPerSec">0</span> |
| </div> |
| </div> |
|
|
| <div class="ai-info"> |
| <h4>🧠 AI Decision Making</h4> |
| <p id="currentDecision">System initializing...</p> |
| <br> |
| <p><strong>Embodied AI Features:</strong></p> |
| <ul style="margin-left: 15px; margin-top: 5px;"> |
| <li>Real-time sensor processing</li> |
| <li>Dynamic path planning</li> |
| <li>Obstacle avoidance</li> |
| <li>Goal-oriented behavior</li> |
| <li>Environmental adaptation</li> |
| </ul> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const canvas = document.getElementById('simulationCanvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| |
| function resizeCanvas() { |
| const container = canvas.parentElement; |
| canvas.width = container.clientWidth; |
| canvas.height = container.clientHeight; |
| } |
| |
| resizeCanvas(); |
| window.addEventListener('resize', resizeCanvas); |
| |
| |
| let isRunning = false; |
| let animationId = null; |
| let lastTime = 0; |
| let decisionCount = 0; |
| let lastDecisionTime = 0; |
| |
| |
| class AutonomousVehicle { |
| constructor(x, y) { |
| this.x = x; |
| this.y = y; |
| this.vx = 0; |
| this.vy = 0; |
| this.angle = 0; |
| this.speed = 2; |
| this.maxSpeed = 5; |
| this.size = 15; |
| this.sensorRange = 100; |
| this.sensors = []; |
| this.path = []; |
| this.currentTarget = null; |
| this.distanceTraveled = 0; |
| this.lastX = x; |
| this.lastY = y; |
| this.behavior = 'normal'; |
| this.avoidanceVector = { x: 0, y: 0 }; |
| } |
| |
| updateSensors() { |
| this.sensors = []; |
| const numSensors = 8; |
| |
| for (let i = 0; i < numSensors; i++) { |
| const angle = (i / numSensors) * 2 * Math.PI; |
| const endX = this.x + Math.cos(angle) * this.sensorRange; |
| const endY = this.y + Math.sin(angle) * this.sensorRange; |
| |
| let distance = this.sensorRange; |
| |
| |
| for (let obstacle of obstacles) { |
| const dx = obstacle.x - this.x; |
| const dy = obstacle.y - this.y; |
| const distToObstacle = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distToObstacle < distance && distToObstacle > 0) { |
| const angleToObstacle = Math.atan2(dy, dx); |
| const angleDiff = Math.abs(angle - angleToObstacle); |
| |
| if (angleDiff < 0.5 || angleDiff > 2 * Math.PI - 0.5) { |
| distance = Math.max(0, distToObstacle - obstacle.size); |
| } |
| } |
| } |
| |
| this.sensors.push({ |
| angle: angle, |
| distance: distance, |
| x: this.x + Math.cos(angle) * distance, |
| y: this.y + Math.sin(angle) * distance |
| }); |
| } |
| } |
| |
| makeDecision() { |
| decisionCount++; |
| |
| |
| let closestTarget = null; |
| let minDistance = Infinity; |
| |
| for (let target of targets) { |
| const dx = target.x - this.x; |
| const dy = target.y - this.y; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distance < minDistance) { |
| minDistance = distance; |
| closestTarget = target; |
| } |
| } |
| |
| this.currentTarget = closestTarget; |
| |
| |
| this.avoidanceVector = { x: 0, y: 0 }; |
| |
| for (let sensor of this.sensors) { |
| if (sensor.distance < this.sensorRange * 0.7) { |
| const avoidanceStrength = (this.sensorRange * 0.7 - sensor.distance) / (this.sensorRange * 0.7); |
| this.avoidanceVector.x -= Math.cos(sensor.angle) * avoidanceStrength; |
| this.avoidanceVector.y -= Math.sin(sensor.angle) * avoidanceStrength; |
| } |
| } |
| |
| |
| const avoidanceMag = Math.sqrt(this.avoidanceVector.x ** 2 + this.avoidanceVector.y ** 2); |
| if (avoidanceMag > 0) { |
| this.avoidanceVector.x /= avoidanceMag; |
| this.avoidanceVector.y /= avoidanceMag; |
| } |
| |
| |
| const decisionElement = document.getElementById('currentDecision'); |
| if (this.currentTarget) { |
| if (avoidanceMag > 0.1) { |
| decisionElement.textContent = `Avoiding obstacles while navigating to target (${Math.round(minDistance)}m away)`; |
| } else { |
| decisionElement.textContent = `Direct path to target (${Math.round(minDistance)}m away)`; |
| } |
| } else { |
| decisionElement.textContent = 'Searching for targets...'; |
| } |
| } |
| |
| update(deltaTime) { |
| this.updateSensors(); |
| this.makeDecision(); |
| |
| |
| let targetVector = { x: 0, y: 0 }; |
| if (this.currentTarget) { |
| const dx = this.currentTarget.x - this.x; |
| const dy = this.currentTarget.y - this.y; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| if (distance > 0) { |
| targetVector.x = dx / distance; |
| targetVector.y = dy / distance; |
| } |
| |
| |
| if (distance < 20) { |
| const index = targets.indexOf(this.currentTarget); |
| if (index > -1) { |
| targets.splice(index, 1); |
| stats.targetsReached++; |
| document.getElementById('targetsReached').textContent = stats.targetsReached; |
| } |
| } |
| } |
| |
| |
| let finalVector = { x: 0, y: 0 }; |
| const behaviorSettings = { |
| cautious: { avoidance: 0.8, target: 0.2 }, |
| normal: { avoidance: 0.6, target: 0.4 }, |
| aggressive: { avoidance: 0.3, target: 0.7 } |
| }; |
| |
| const settings = behaviorSettings[this.behavior]; |
| finalVector.x = targetVector.x * settings.target + this.avoidanceVector.x * settings.avoidance; |
| finalVector.y = targetVector.y * settings.target + this.avoidanceVector.y * settings.avoidance; |
| |
| |
| const finalMag = Math.sqrt(finalVector.x ** 2 + finalVector.y ** 2); |
| if (finalMag > 0) { |
| finalVector.x /= finalMag; |
| finalVector.y /= finalMag; |
| } |
| |
| |
| this.vx = finalVector.x * this.speed; |
| this.vy = finalVector.y * this.speed; |
| |
| |
| this.x += this.vx * deltaTime; |
| this.y += this.vy * deltaTime; |
| |
| |
| this.x = Math.max(this.size, Math.min(canvas.width - this.size, this.x)); |
| this.y = Math.max(this.size, Math.min(canvas.height - this.size, this.y)); |
| |
| |
| if (this.vx !== 0 || this.vy !== 0) { |
| this.angle = Math.atan2(this.vy, this.vx); |
| } |
| |
| |
| const dx = this.x - this.lastX; |
| const dy = this.y - this.lastY; |
| this.distanceTraveled += Math.sqrt(dx * dx + dy * dy); |
| this.lastX = this.x; |
| this.lastY = this.y; |
| } |
| |
| draw() { |
| |
| ctx.strokeStyle = 'rgba(255, 193, 7, 0.3)'; |
| ctx.lineWidth = 1; |
| for (let sensor of this.sensors) { |
| ctx.beginPath(); |
| ctx.moveTo(this.x, this.y); |
| ctx.lineTo(sensor.x, sensor.y); |
| ctx.stroke(); |
| } |
| |
| |
| ctx.save(); |
| ctx.translate(this.x, this.y); |
| ctx.rotate(this.angle); |
| |
| |
| ctx.fillStyle = '#4CAF50'; |
| ctx.fillRect(-this.size, -this.size/2, this.size * 2, this.size); |
| |
| |
| ctx.fillStyle = '#fff'; |
| ctx.fillRect(this.size/2, -this.size/4, this.size/2, this.size/2); |
| |
| ctx.restore(); |
| |
| |
| if (this.path.length > 1) { |
| ctx.strokeStyle = 'rgba(76, 175, 80, 0.5)'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| ctx.moveTo(this.path[0].x, this.path[0].y); |
| for (let i = 1; i < this.path.length; i++) { |
| ctx.lineTo(this.path[i].x, this.path[i].y); |
| } |
| ctx.stroke(); |
| } |
| } |
| } |
| |
| |
| class Obstacle { |
| constructor(x, y, size = 20) { |
| this.x = x; |
| this.y = y; |
| this.size = size; |
| } |
| |
| draw() { |
| ctx.fillStyle = '#f44336'; |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.strokeStyle = '#fff'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| ctx.moveTo(this.x - this.size/2, this.y - this.size/2); |
| ctx.lineTo(this.x + this.size/2, this.y + this.size/2); |
| ctx.moveTo(this.x + this.size/2, this.y - this.size/2); |
| ctx.lineTo(this.x - this.size/2, this.y + this.size/2); |
| ctx.stroke(); |
| } |
| } |
| |
| |
| class Target { |
| constructor(x, y) { |
| this.x = x; |
| this.y = y; |
| this.size = 15; |
| this.pulsePhase = Math.random() * Math.PI * 2; |
| } |
| |
| update(deltaTime) { |
| this.pulsePhase += deltaTime * 0.003; |
| } |
| |
| draw() { |
| const pulse = Math.sin(this.pulsePhase) * 0.3 + 1; |
| const size = this.size * pulse; |
| |
| ctx.fillStyle = '#2196F3'; |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, size, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.strokeStyle = '#fff'; |
| ctx.lineWidth = 2; |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, size * 0.7, 0, Math.PI * 2); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, size * 0.4, 0, Math.PI * 2); |
| ctx.stroke(); |
| } |
| } |
| |
| |
| const vehicle = new AutonomousVehicle(100, 100); |
| const obstacles = []; |
| const targets = []; |
| |
| |
| const stats = { |
| distanceTraveled: 0, |
| targetsReached: 0, |
| obstaclesAvoided: 0, |
| decisionsPerSec: 0 |
| }; |
| |
| |
| function initializeEnvironment() { |
| obstacles.length = 0; |
| targets.length = 0; |
| |
| |
| obstacles.push(new Obstacle(300, 200, 25)); |
| obstacles.push(new Obstacle(500, 350, 30)); |
| obstacles.push(new Obstacle(200, 400, 20)); |
| obstacles.push(new Obstacle(600, 150, 35)); |
| |
| |
| targets.push(new Target(400, 100)); |
| targets.push(new Target(600, 400)); |
| targets.push(new Target(150, 300)); |
| } |
| |
| |
| document.getElementById('startBtn').addEventListener('click', () => { |
| isRunning = true; |
| animate(); |
| }); |
| |
| document.getElementById('pauseBtn').addEventListener('click', () => { |
| isRunning = false; |
| if (animationId) { |
| cancelAnimationFrame(animationId); |
| } |
| }); |
| |
| document.getElementById('resetBtn').addEventListener('click', () => { |
| isRunning = false; |
| if (animationId) { |
| cancelAnimationFrame(animationId); |
| } |
| |
| |
| vehicle.x = 100; |
| vehicle.y = 100; |
| vehicle.vx = 0; |
| vehicle.vy = 0; |
| vehicle.distanceTraveled = 0; |
| vehicle.path = []; |
| |
| |
| stats.targetsReached = 0; |
| stats.obstaclesAvoided = 0; |
| decisionCount = 0; |
| |
| |
| initializeEnvironment(); |
| |
| |
| updateStatsDisplay(); |
| }); |
| |
| document.getElementById('speedSlider').addEventListener('input', (e) => { |
| vehicle.speed = parseFloat(e.target.value); |
| document.getElementById('speedValue').textContent = vehicle.speed.toFixed(1); |
| }); |
| |
| document.getElementById('sensorRange').addEventListener('input', (e) => { |
| vehicle.sensorRange = parseInt(e.target.value); |
| document.getElementById('sensorValue').textContent = vehicle.sensorRange; |
| }); |
| |
| document.getElementById('behaviorSelect').addEventListener('change', (e) => { |
| vehicle.behavior = e.target.value; |
| }); |
| |
| document.getElementById('addObstacleBtn').addEventListener('click', () => { |
| const x = Math.random() * (canvas.width - 100) + 50; |
| const y = Math.random() * (canvas.height - 100) + 50; |
| const size = Math.random() * 20 + 15; |
| obstacles.push(new Obstacle(x, y, size)); |
| }); |
| |
| document.getElementById('addTargetBtn').addEventListener('click', () => { |
| const x = Math.random() * (canvas.width - 100) + 50; |
| const y = Math.random() * (canvas.height - 100) + 50; |
| targets.push(new Target(x, y)); |
| }); |
| |
| document.getElementById('clearBtn').addEventListener('click', () => { |
| obstacles.length = 0; |
| targets.length = 0; |
| }); |
| |
| |
| canvas.addEventListener('click', (e) => { |
| const rect = canvas.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| |
| if (e.shiftKey) { |
| obstacles.push(new Obstacle(x, y, 20)); |
| } else { |
| targets.push(new Target(x, y)); |
| } |
| }); |
| |
| |
| function updateStatsDisplay() { |
| document.getElementById('distanceTraveled').textContent = Math.round(vehicle.distanceTraveled) + 'm'; |
| document.getElementById('targetsReached').textContent = stats.targetsReached; |
| document.getElementById('obstaclesAvoided').textContent = stats.obstaclesAvoided; |
| document.getElementById('decisionsPerSec').textContent = stats.decisionsPerSec; |
| } |
| |
| |
| function animate(currentTime = 0) { |
| if (!isRunning) return; |
| |
| const deltaTime = currentTime - lastTime; |
| lastTime = currentTime; |
| |
| |
| if (currentTime - lastDecisionTime > 1000) { |
| stats.decisionsPerSec = Math.round((decisionCount / (currentTime - lastDecisionTime)) * 1000); |
| decisionCount = 0; |
| lastDecisionTime = currentTime; |
| } |
| |
| |
| ctx.fillStyle = '#1a1a2e'; |
| ctx.fillRect(0, 0, canvas.width, canvas.height); |
| |
| |
| for (let target of targets) { |
| target.update(deltaTime); |
| target.draw(); |
| } |
| |
| |
| for (let obstacle of obstacles) { |
| obstacle.draw(); |
| } |
| |
| |
| vehicle.update(deltaTime); |
| vehicle.draw(); |
| |
| |
| updateStatsDisplay(); |
| |
| animationId = requestAnimationFrame(animate); |
| } |
| |
| |
| initializeEnvironment(); |
| animate(); |
| |
| |
| const instructions = document.createElement('div'); |
| instructions.style.cssText = ` |
| position: fixed; |
| top: 20px; |
| right: 20px; |
| background: rgba(0, 0, 0, 0.8); |
| color: white; |
| padding: 15px; |
| border-radius: 10px; |
| font-size: 12px; |
| max-width: 200px; |
| z-index: 1000; |
| `; |
| instructions.innerHTML = ` |
| <strong>Controls:</strong><br> |
| • Click to add targets<br> |
| • Shift+Click to add obstacles<br> |
| • Use panel controls for settings<br> |
| • Watch AI make real-time decisions! |
| `; |
| document.body.appendChild(instructions); |
| |
| |
| setTimeout(() => { |
| instructions.style.opacity = '0'; |
| instructions.style.transition = 'opacity 1s'; |
| setTimeout(() => instructions.remove(), 1000); |
| }, 5000); |
| </script> |
| </body> |
| </html> |