<!DOCTYPE html>
<html lang="en" class="h-full dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D DNA Double-Helix & Molecular Structure Lab - Three.js</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@600;700;800&display=swap" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
body { margin: 0; background: #020617; overflow: hidden; font-family: 'Inter', sans-serif; }
#dnaCanvas { width: 100%; height: 100%; cursor: grab; touch-action: none; }
</style>
</head>
<body class="h-screen text-white flex flex-col select-none">
<!-- Header -->
<header class="h-14 bg-slate-900/90 backdrop-blur-md border-b border-slate-800 px-4 sm:px-6 flex items-center justify-between z-20">
<div class="flex items-center gap-3">
<span class="w-9 h-9 rounded-xl bg-gradient-to-tr from-emerald-500 to-cyan-500 flex items-center justify-center text-lg shadow-lg">🧬</span>
<div>
<h1 class="font-bold text-sm sm:text-base text-white">3D DNA Double-Helix Molecular Lab</h1>
<p class="text-[10px] text-emerald-400 font-mono">Interactive Base-Pair Nucleotide Visualizer</p>
</div>
</div>
<div class="flex items-center gap-3 text-xs">
<span class="px-2.5 py-1 rounded-md bg-cyan-500/20 text-cyan-300 font-mono">Adenine (A) - Thymine (T)</span>
<span class="hidden sm:inline-block px-2.5 py-1 rounded-md bg-rose-500/20 text-rose-300 font-mono">Guanine (G) - Cytosine (C)</span>
</div>
</header>
<div class="flex-1 relative overflow-hidden">
<canvas id="dnaCanvas"></canvas>
<div class="absolute bottom-5 left-1/2 -translate-x-1/2 bg-slate-900/80 backdrop-blur-md border border-slate-800 rounded-full px-5 py-2 text-xs text-slate-300 pointer-events-none">
🧬 Drag to Rotate 3D DNA Strand • Scroll to Zoom
</div>
</div>
<script>
const canvas = document.getElementById('dnaCanvas');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / (window.innerHeight - 56), 0.1, 1000);
camera.position.set(0, 0, 12);
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight - 56);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
const dnaGroup = new THREE.Group();
scene.add(dnaGroup);
const atomGeo = new THREE.SphereGeometry(0.24, 20, 20);
const bondGeo = new THREE.CylinderGeometry(0.06, 0.06, 1.4, 12);
const matBlue = new THREE.MeshStandardMaterial({ color: 0x06b6d4, metalness: 0.5, roughness: 0.2 });
const matPink = new THREE.MeshStandardMaterial({ color: 0xf43f5e, metalness: 0.5, roughness: 0.2 });
const matGreen = new THREE.MeshStandardMaterial({ color: 0x10b981, metalness: 0.5, roughness: 0.2 });
const matAmber = new THREE.MeshStandardMaterial({ color: 0xf59e0b, metalness: 0.5, roughness: 0.2 });
for (let i = -18; i <= 18; i++) {
const y = i * 0.36;
const angle = i * 0.38;
const r = 1.4;
const a1 = new THREE.Mesh(atomGeo, i % 2 === 0 ? matBlue : matGreen);
a1.position.set(Math.cos(angle) * r, y, Math.sin(angle) * r);
dnaGroup.add(a1);
const a2 = new THREE.Mesh(atomGeo, i % 2 === 0 ? matPink : matAmber);
a2.position.set(Math.cos(angle + Math.PI) * r, y, Math.sin(angle + Math.PI) * r);
dnaGroup.add(a2);
// Base Pair Bridge
const bond = new THREE.Mesh(bondGeo, new THREE.MeshStandardMaterial({ color: 0x94a3b8 }));
bond.position.set(0, y, 0);
bond.rotation.z = Math.PI / 2;
bond.rotation.y = -angle;
dnaGroup.add(bond);
}
scene.add(new THREE.AmbientLight(0xffffff, 0.7));
const light = new THREE.DirectionalLight(0xffffff, 1.4);
light.position.set(5, 5, 8);
scene.add(light);
let dragging = false, prevX = 0, prevY = 0;
canvas.addEventListener('pointerdown', e => { dragging = true; prevX = e.clientX; prevY = e.clientY; });
window.addEventListener('pointermove', e => {
if (!dragging) return;
dnaGroup.rotation.y += (e.clientX - prevX) * 0.008;
dnaGroup.rotation.x += (e.clientY - prevY) * 0.008;
prevX = e.clientX; prevY = e.clientY;
});
window.addEventListener('pointerup', () => dragging = false);
function animate() {
requestAnimationFrame(animate);
if (!dragging) dnaGroup.rotation.y += 0.008;
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / (window.innerHeight - 56);
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight - 56);
});
</script>
</body>
</html>
/* Built-in 3D WebGL & Glassmorphic Shaders included in index.html */
// Complete Three.js 3D scene, camera, lighting, and pointer controls included