Gemini 3.7 Flash Released: Specs, Benchmarks & Interactive Coding Tests
Google released Gemini 3.7 Flash today, August 13, 2026. Featuring a 1M token context window, $0.75/$3.75 pricing, and an Artificial Analysis score of 56, we run hands-on vector SVG and 3D WebGL game coding tests.
Mohid Mirza
Co-Founder & Lead Programmer of AcceleratedLogic AI
Model Specs & Pricing
Test 1: Photorealistic 3D Glass Sphere with Origami Crane
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photorealistic 3D Glass Sphere with Origami Crane</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: radial-gradient(circle at 50% 30%, #16192b 0%, #090a10 70%, #030407 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.container {
position: relative;
width: 100vw;
height: 100vh;
max-width: 1000px;
max-height: 1000px;
display: flex;
justify-content: center;
align-items: center;
}
svg {
width: 100%;
height: 100%;
filter: drop-shadow(0 30px 50px rgba(0, 0, 0, 0.8));
}
</style>
</head>
<body>
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="100%" height="100%">
<defs>
<!-- Ground & Ambient Shadows -->
<radialGradient id="groundFloor" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#000000" stop-opacity="0.7"/>
<stop offset="40%" stop-color="#000000" stop-opacity="0.4"/>
<stop offset="75%" stop-color="#04060a" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#04060a" stop-opacity="0"/>
</radialGradient>
<radialGradient id="contactShadow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#000000" stop-opacity="0.95"/>
<stop offset="25%" stop-color="#000000" stop-opacity="0.7"/>
<stop offset="60%" stop-color="#020408" stop-opacity="0.2"/>
<stop offset="100%" stop-color="#020408" stop-opacity="0"/>
</radialGradient>
<!-- Caustic Glow Under Glass -->
<radialGradient id="causticGround" cx="45%" cy="45%" r="50%">
<stop offset="0%" stop-color="#6df0ff" stop-opacity="0.55"/>
<stop offset="25%" stop-color="#2d8bba" stop-opacity="0.3"/>
<stop offset="55%" stop-color="#c58bff" stop-opacity="0.12"/>
<stop offset="85%" stop-color="#0a1828" stop-opacity="0.03"/>
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
</radialGradient>
<!-- Sphere Environment Base & Rim Backing -->
<radialGradient id="sphereBackdrop" cx="38%" cy="32%" r="68%">
<stop offset="0%" stop-color="#1b2438" stop-opacity="0.25"/>
<stop offset="45%" stop-color="#0a101d" stop-opacity="0.5"/>
<stop offset="75%" stop-color="#04070f" stop-opacity="0.75"/>
<stop offset="92%" stop-color="#152942" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#4ce8ff" stop-opacity="0.95"/>
</radialGradient>
<!-- Dark Inner Absorption Gradient (Thickness) -->
<radialGradient id="glassThickness" cx="50%" cy="50%" r="50%">
<stop offset="70%" stop-color="#000000" stop-opacity="0"/>
<stop offset="88%" stop-color="#020914" stop-opacity="0.35"/>
<stop offset="96%" stop-color="#08223c" stop-opacity="0.7"/>
<stop offset="100%" stop-color="#3ae7ff" stop-opacity="0.9"/>
</radialGradient>
<!-- Inner Caustic Orb -->
<radialGradient id="innerCausticGlow" cx="42%" cy="65%" r="45%">
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.45"/>
<stop offset="25%" stop-color="#55e1ff" stop-opacity="0.25"/>
<stop offset="55%" stop-color="#9d4edd" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
</radialGradient>
<!-- Front Glass Fresnel Reflection Envelope -->
<radialGradient id="fresnelFront" cx="35%" cy="30%" r="65%">
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.15"/>
<stop offset="40%" stop-color="#ffffff" stop-opacity="0.02"/>
<stop offset="75%" stop-color="#4aa8ff" stop-opacity="0.08"/>
<stop offset="94%" stop-color="#70e5ff" stop-opacity="0.4"/>
<stop offset="98%" stop-color="#ffffff" stop-opacity="0.85"/>
<stop offset="100%" stop-color="#2675a8" stop-opacity="0.2"/>
</radialGradient>
<!-- Primary Key Light Highlight (Softbox Studio Reflection) -->
<linearGradient id="mainSoftbox" x1="0%" y1="0%" x2="70%" y2="80%">
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
<stop offset="20%" stop-color="#eaf9ff" stop-opacity="0.8"/>
<stop offset="55%" stop-color="#80d4ff" stop-opacity="0.25"/>
<stop offset="85%" stop-color="#ffffff" stop-opacity="0"/>
</linearGradient>
<!-- Secondary Backlight Highlight -->
<linearGradient id="secondaryGlow" x1="100%" y1="100%" x2="20%" y2="20%">
<stop offset="0%" stop-color="#845ec2" stop-opacity="0.6"/>
<stop offset="35%" stop-color="#d65db1" stop-opacity="0.2"/>
<stop offset="70%" stop-color="#ff6f91" stop-opacity="0"/>
</linearGradient>
<!-- Bottom Ground Bounce / Refraction Arch -->
<linearGradient id="bottomBounce" x1="0%" y1="100%" x2="0%" y2="0%">
<stop offset="0%" stop-color="#4eedff" stop-opacity="0.75"/>
<stop offset="20%" stop-color="#005b99" stop-opacity="0.4"/>
<stop offset="60%" stop-color="#001829" stop-opacity="0"/>
</linearGradient>
<!-- ================= ORIGAMI CRANE GRADIENTS ================= -->
<!-- Paper Textures and Realistic Light/Shade Facets -->
<!-- Left Wing Main Top (Keylight Lit) -->
<linearGradient id="p_left_wing_top" x1="30%" y1="10%" x2="80%" y2="90%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="45%" stop-color="#e3ecf5"/>
<stop offset="100%" stop-color="#adc4d9"/>
</linearGradient>
<!-- Left Wing Outer Fold -->
<linearGradient id="p_left_wing_mid" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#e1ebf5"/>
<stop offset="50%" stop-color="#bacddb"/>
<stop offset="100%" stop-color="#7d9cb8"/>
</linearGradient>
<!-- Left Wing Underside Shadow -->
<linearGradient id="p_left_wing_under" x1="0%" y1="30%" x2="100%" y2="70%">
<stop offset="0%" stop-color="#46627f"/>
<stop offset="60%" stop-color="#2d4359"/>
<stop offset="100%" stop-color="#1d2e3f"/>
</linearGradient>
<!-- Right Wing Back Turn -->
<linearGradient id="p_right_wing_top" x1="10%" y1="0%" x2="90%" y2="100%">
<stop offset="0%" stop-color="#c2daf0"/>
<stop offset="50%" stop-color="#8aa6bf"/>
<stop offset="100%" stop-color="#46627a"/>
</linearGradient>
<linearGradient id="p_right_wing_back" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#799cb8"/>
<stop offset="100%" stop-color="#3c5770"/>
</linearGradient>
<!-- Body Facets -->
<linearGradient id="p_body_front_left" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="60%" stop-color="#d6e4f0"/>
<stop offset="100%" stop-color="#9ab5cc"/>
</linearGradient>
<linearGradient id="p_body_front_right" x1="0%" y1="0%" x2="80%" y2="100%">
<stop offset="0%" stop-color="#bacfe3"/>
<stop offset="60%" stop-color="#7d9db8"/>
<stop offset="100%" stop-color="#4e6d87"/>
</linearGradient>
<linearGradient id="p_body_keel_dark" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#334b61"/>
<stop offset="100%" stop-color="#1c2d3d"/>
</linearGradient>
<linearGradient id="p_body_top_crease" x1="0%" y1="0%" x2="100%" y2="50%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="100%" stop-color="#c9dded"/>
</linearGradient>
<!-- Tail Facets -->
<linearGradient id="p_tail_left" x1="20%" y1="0%" x2="80%" y2="100%">
<stop offset="0%" stop-color="#dbe8f5"/>
<stop offset="50%" stop-color="#9abddb"/>
<stop offset="100%" stop-color="#557591"/>
</linearGradient>
<linearGradient id="p_tail_underside" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3a566f"/>
<stop offset="100%" stop-color="#1a2b38"/>
</linearGradient>
<!-- Neck Facets -->
<linearGradient id="p_neck_left" x1="30%" y1="0%" x2="70%" y2="100%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="40%" stop-color="#d6e5f2"/>
<stop offset="100%" stop-color="#95b5cf"/>
</linearGradient>
<linearGradient id="p_neck_right" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#8cb0cc"/>
<stop offset="70%" stop-color="#4b6d87"/>
<stop offset="100%" stop-color="#2c4457"/>
</linearGradient>
<!-- Head & Beak Facets -->
<linearGradient id="p_head_crown" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="60%" stop-color="#c5ddf0"/>
<stop offset="100%" stop-color="#80a5c4"/>
</linearGradient>
<linearGradient id="p_head_side" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#a4c6e3"/>
<stop offset="100%" stop-color="#3a5870"/>
</linearGradient>
<linearGradient id="p_beak_tip" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffb88c"/>
<stop offset="100%" stop-color="#de6262"/>
</linearGradient>
<!-- Internal Crane Warm Subsurface Glow -->
<radialGradient id="craneSubsurface" cx="45%" cy="45%" r="50%">
<stop offset="0%" stop-color="#ffeedd" stop-opacity="0.3"/>
<stop offset="60%" stop-color="#8adfff" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
</radialGradient>
<!-- Filters for Realistic Optics -->
<!-- Deep Blur for Distant Dropped Shadows -->
<filter id="blurGround" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="38"/>
</filter>
<filter id="blurContact" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="8"/>
</filter>
<filter id="blurCaustic" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="18"/>
</filter>
<filter id="craneInternalShadow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
<feOffset dx="12" dy="25"/>
<feComponentTransfer><feFuncA type="linear" slope="0.45"/></feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="softGlow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="6" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Sphere Clip Path to Contain Internal Elements -->
<clipPath id="sphereInteriorClip">
<circle cx="500" cy="460" r="260"/>
</clipPath>
</defs>
<!-- ================= 1. GROUND SHADOWS & CAUSTICS ================= -->
<g id="groundPlane">
<!-- Broad ambient ground shadow -->
<ellipse cx="500" cy="775" rx="360" ry="85" fill="url(#groundFloor)" filter="url(#blurGround)"/>
<!-- Vibrant caustic focus pool beneath the sphere -->
<ellipse cx="485" cy="765" rx="210" ry="52" fill="url(#causticGround)" filter="url(#blurCaustic)" style="mix-blend-mode: screen;"/>
<ellipse cx="480" cy="760" rx="120" ry="28" fill="#a6f5ff" opacity="0.45" filter="url(#blurContact)" style="mix-blend-mode: screen;"/>
<ellipse cx="475" cy="758" rx="55" ry="14" fill="#ffffff" opacity="0.75" filter="url(#blurContact)" style="mix-blend-mode: screen;"/>
<!-- Tight contact occlusion shadow right at touch point -->
<ellipse cx="500" cy="720" rx="85" ry="14" fill="url(#contactShadow)" filter="url(#blurContact)"/>
<ellipse cx="500" cy="720" rx="42" ry="6" fill="#000000" opacity="0.95"/>
</g>
<!-- ================= 2. GLASS SPHERE BACK LAYER ================= -->
<g id="sphereBackground">
<!-- Base Outer Rim Boundary -->
<circle cx="500" cy="460" r="260" fill="url(#sphereBackdrop)"/>
<!-- Inner Backlight/Internal Caustic Core -->
<circle cx="500" cy="460" r="258" fill="url(#innerCausticGlow)" style="mix-blend-mode: screen;"/>
</g>
<!-- ================= 3. INTERNAL 3D ORIGAMI CRANE (Inside Glass) ================= -->
<g id="internalScene" clip-path="url(#sphereInteriorClip)">
<!-- Ambient Internal Light Field -->
<radialGradient id="craneBackGlow" cx="420" cy="400" r="250" gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#48a6ff" stop-opacity="0.3"/>
<stop offset="50%" stop-color="#733add" stop-opacity="0.1"/>
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
</radialGradient>
<circle cx="500" cy="460" r="260" fill="url(#craneBackGlow)" style="mix-blend-mode: screen;"/>
<!-- Internal Crane Drop Shadow on Back Inner Glass Wall -->
<g opacity="0.55" transform="translate(18, 32) scale(0.96) skewX(-4)" filter="url(#blurContact)">
<path d="M 500,485 L 340,360 L 260,335 L 310,380 L 445,475 L 430,555 L 485,595 L 500,600 L 515,595 L 570,555 L 555,475 L 690,380 L 740,335 L 660,360 Z" fill="#030c17"/>
</g>
<!-- THE 3D ORIGAMI CRANE -->
<g id="origamiCrane" filter="url(#craneInternalShadow)">
<!-- === REAR PARTS (Distant / Shadowed Facets) === -->
<!-- Tail - Underside / Back Fold -->
<polygon points="500,485 555,475 675,370 540,515" fill="url(#p_tail_underside)"/>
<!-- Tail - Left/Upper Plane (Facing Light) -->
<polygon points="500,485 675,370 705,355 530,520" fill="url(#p_tail_left)" stroke="#9bc7e8" stroke-width="0.35" stroke-linejoin="round"/>
<!-- Right Wing (Distant Wing - Tilted Away in Perspective) -->
<!-- Right Wing Lower Hull Fold -->
<polygon points="500,485 540,515 680,260" fill="url(#p_right_wing_back)"/>
<!-- Right Wing Main Triangular Blade -->
<polygon points="500,460 500,485 680,260" fill="url(#p_right_wing_top)" stroke="#8cbfe6" stroke-width="0.4"/>
<!-- Right Wing Secondary Upper Facet -->
<polygon points="500,460 680,260 625,245" fill="#a4cbe8" opacity="0.9"/>
<!-- === CENTRAL BODY STRUCTURE === -->
<!-- Body Under-Keel Keystones (Shadow Anchor) -->
<polygon points="500,580 470,525 500,545" fill="url(#p_body_keel_dark)"/>
<polygon points="500,580 530,525 500,545" fill="#131e28"/>
<!-- Body Low Side Triangles -->
<polygon points="445,490 470,525 500,545 470,490" fill="#4d6b85"/>
<polygon points="555,490 530,525 500,545 530,490" fill="#2d4254"/>
<!-- Body Central Diamond - Left Half (Main Lit Plane) -->
<polygon points="500,460 445,490 470,530 500,575" fill="url(#p_body_front_left)" stroke="#ffffff" stroke-width="0.5" stroke-linejoin="round"/>
<!-- Body Central Diamond - Right Half (Shaded Plane) -->
<polygon points="500,460 555,490 530,530 500,575" fill="url(#p_body_front_right)" stroke="#a1c3de" stroke-width="0.5" stroke-linejoin="round"/>
<!-- Center Dorsal Ridge Pyramid -->
<polygon points="500,460 470,490 500,505" fill="url(#p_body_top_crease)"/>
<polygon points="500,460 530,490 500,505" fill="#88a8c4"/>
<!-- === FOREGROUND LEFT WING (Spectacular Fold Dynamics) === -->
<!-- Left Wing Underside Drop Triangle -->
<polygon points="500,460 445,490 310,230" fill="url(#p_left_wing_under)"/>
<!-- Left Wing Main Facet (Reflecting Vast Light) -->
<polygon points="500,460 460,450 280,210 310,230" fill="url(#p_left_wing_top)" stroke="#ffffff" stroke-width="0.6" stroke-linejoin="round"/>
<!-- Left Wing Outer Crisp Edge Blade -->
<polygon points="460,450 280,210 245,220 375,385" fill="url(#p_left_wing_mid)" stroke="#e1f0fa" stroke-width="0.4"/>
<!-- Left Wing Inner Accordion Gusset -->
<polygon points="445,490 460,450 375,385 410,465" fill="#698ca8"/>
<!-- === NECK, HEAD AND BEAK === -->
<!-- Neck Base Trunk -->
<polygon points="500,485 460,480 370,390 435,465" fill="#7599b8"/>
<!-- Neck Long Column - Left Lit Edge -->
<polygon points="460,480 370,390 340,320 375,335" fill="url(#p_neck_left)" stroke="#ffffff" stroke-width="0.4" stroke-linejoin="round"/>
<!-- Neck Long Column - Right Shaded Flange -->
<polygon points="500,485 435,465 375,335 405,345" fill="url(#p_neck_right)" stroke="#5d83a1" stroke-width="0.3"/>
<!-- Head Reverse Fold Peak -->
<polygon points="340,320 375,335 345,305 315,310" fill="url(#p_head_crown)" stroke="#ffffff" stroke-width="0.4"/>
<polygon points="375,335 405,345 355,315 345,305" fill="url(#p_head_side)"/>
<!-- Folded Beak Drooping Forward -->
<polygon points="315,310 345,305 285,325" fill="url(#p_beak_tip)" stroke="#ffe1cc" stroke-width="0.3"/>
<polygon points="345,305 355,315 285,325" fill="#ab3c3c"/>
<!-- Micro Edge Highlights (Origami Paper Crease Crispness) -->
<polyline points="280,210 500,460 680,260" fill="none" stroke="#ffffff" stroke-width="0.9" opacity="0.85" filter="url(#softGlow)"/>
<polyline points="285,325 345,305 375,335 460,480 500,575" fill="none" stroke="#ffffff" stroke-width="0.75" opacity="0.7"/>
<polyline points="500,460 500,575" fill="none" stroke="#ffffff" stroke-width="0.8" opacity="0.9"/>
<polyline points="705,355 500,485" fill="none" stroke="#e0f2fe" stroke-width="0.6" opacity="0.6"/>
<!-- Delicate Origami Paper Texture / Translucency Sheen -->
<ellipse cx="440" cy="420" rx="90" ry="120" fill="url(#craneSubsurface)" style="mix-blend-mode: screen; pointer-events: none;"/>
</g>
<!-- Internal Caustic Rings & Spherical Internal Aberration Rays -->
<path d="M 280,420 Q 320,620 500,680 Q 660,630 710,450 C 660,600 520,650 350,560 Z" fill="#2be7ff" opacity="0.25" style="mix-blend-mode: screen;" filter="url(#blurContact)"/>
<path d="M 320,380 Q 400,670 600,640 C 460,660 360,540 320,380 Z" fill="#b04eff" opacity="0.2" style="mix-blend-mode: screen;" filter="url(#blurContact)"/>
<!-- Internal Floating Micro Refraction Specks -->
<circle cx="360" cy="480" r="2.5" fill="#ffffff" opacity="0.6" filter="url(#softGlow)"/>
<circle cx="580" cy="380" r="1.8" fill="#ffffff" opacity="0.7" filter="url(#softGlow)"/>
<circle cx="420" cy="270" r="1.5" fill="#aae7ff" opacity="0.5"/>
<circle cx="630" cy="510" r="2" fill="#88f4ff" opacity="0.6"/>
<circle cx="490" cy="620" r="2.2" fill="#ffffff" opacity="0.4" filter="url(#softGlow)"/>
</g>
<!-- ================= 4. GLASS THICKNESS & FRONT REFRACTIONS ================= -->
<!-- 3D Glass Shell Body Absorptive Thickness Ring -->
<circle cx="500" cy="460" r="260" fill="url(#glassThickness)" style="mix-blend-mode: multiply;"/>
<circle cx="500" cy="460" r="260" fill="url(#glassThickness)" opacity="0.6" style="mix-blend-mode: color-dodge;"/>
<!-- Front Fresnel Translucent Skin -->
<circle cx="500" cy="460" r="260" fill="url(#fresnelFront)" style="mix-blend-mode: screen;"/>
<!-- ================= 5. CURVED SPECULAR GLOSS & REFLECTIONS (The 3D Glass Polish) ================= -->
<!-- Glass Bottom Ambient Ground Bounce Light Arc -->
<path d="M 290,560 C 330,680 430,718 500,718 C 570,718 670,680 710,560 C 665,660 580,698 500,698 C 420,698 335,660 290,560 Z" fill="url(#bottomBounce)" style="mix-blend-mode: screen;"/>
<path d="M 370,660 C 430,705 570,705 630,660 C 575,688 425,688 370,660 Z" fill="#ffffff" opacity="0.65" filter="url(#blurContact)" style="mix-blend-mode: screen;"/>
<!-- Glass Rim Thin Edge Highlight (Surround Fresnel Rim) -->
<circle cx="500" cy="460" r="258.5" fill="none" stroke="url(#mainSoftbox)" stroke-width="2.5" opacity="0.85" style="mix-blend-mode: screen;"/>
<!-- Secondary Magenta/Violet Edge Rim Light (Right Flank) -->
<path d="M 680,280 C 745,350 765,470 730,570 C 715,500 710,400 660,330 Z" fill="url(#secondaryGlow)" opacity="0.7" style="mix-blend-mode: screen;" filter="url(#softGlow)"/>
<!-- PRIMARY STUDIO SOFTBOX REFLECTION (Top-Left Curved Specular) -->
<g style="mix-blend-mode: screen;">
<!-- Big Smooth Curved Window Highlight -->
<path d="M 310,285 C 335,225 400,195 470,205 C 430,225 385,255 360,305 C 345,335 340,380 345,415 C 325,375 320,325 310,285 Z" fill="url(#mainSoftbox)" filter="url(#blurContact)"/>
<!-- Intense Hotspot Center Flare inside Softbox -->
<path d="M 345,265 C 365,225 415,212 455,218 C 425,232 390,252 375,285 C 365,308 362,342 366,365 C 352,335 348,295 345,265 Z" fill="#ffffff" opacity="0.95" filter="url(#softGlow)"/>
<!-- Super Hot Tiny Specular Apex Core -->
<ellipse cx="378" cy="265" rx="18" ry="32" transform="rotate(-30 378 265)" fill="#ffffff"/>
<circle cx="378" cy="265" r="45" fill="#8ae3ff" opacity="0.5" filter="url(#blurContact)"/>
</g>
<!-- Secondary Overhead Pinpoint Light Spot -->
<ellipse cx="490" cy="212" rx="45" ry="6" fill="#ffffff" opacity="0.4" filter="url(#softGlow)" style="mix-blend-mode: screen;"/>
<ellipse cx="262" cy="450" rx="4" ry="38" transform="rotate(8 262 450)" fill="#78d5ff" opacity="0.5" filter="url(#softGlow)" style="mix-blend-mode: screen;"/>
<!-- Subtle Front Diagonal Sheen Visor (Gives Spherical Glass Volume) -->
<path d="M 275,360 C 360,250 560,240 700,310 C 600,270 410,280 300,420 C 285,400 280,380 275,360 Z" fill="#ffffff" opacity="0.12" style="mix-blend-mode: screen;" filter="url(#blurContact)"/>
</svg>
</div>
</body>
</html>
Test 2: 3D Procedural Globe Generation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Procedural Globe</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
}
body {
background-color: #030308;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
color: #ffffff;
width: 100vw;
height: 100vh;
}
#canvas-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
/* Glassmorphism UI Overlay */
.ui-panel {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: rgba(13, 17, 28, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 16px;
padding: 16px 24px;
display: flex;
flex-direction: column;
gap: 10px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6), 0 0 20px rgba(64, 150, 255, 0.1);
min-width: 280px;
z-index: 10;
}
.ui-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.85rem;
letter-spacing: 0.05em;
text-transform: uppercase;
color: #8da2c0;
}
.ui-header span#speed-display {
color: #4facfe;
font-weight: 600;
font-family: monospace;
font-size: 0.95rem;
}
.slider-container {
display: flex;
align-items: center;
gap: 12px;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
border-radius: 3px;
background: #1e293b;
outline: none;
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #00f2fe;
box-shadow: 0 0 10px #00f2fe, 0 0 20px rgba(0, 242, 254, 0.4);
cursor: pointer;
transition: transform 0.1s ease;
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.15);
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #00f2fe;
border: none;
box-shadow: 0 0 10px #00f2fe, 0 0 20px rgba(0, 242, 254, 0.4);
cursor: pointer;
}
.info-hint {
position: absolute;
top: 20px;
left: 20px;
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.4);
pointer-events: none;
letter-spacing: 0.04em;
}
</style>
<!-- Three.js and OrbitControls via CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<div id="canvas-container"></div>
<div class="info-hint">DRAG TO ROTATE • SCROLL TO ZOOM</div>
<div class="ui-panel">
<div class="ui-header">
<span>Rotation Speed</span>
<span id="speed-display">1.0x</span>
</div>
<div class="slider-container">
<input type="range" id="rotation-slider" min="0" max="4" step="0.1" value="1.0">
</div>
</div>
<script>
// --- FAST 3D SIMPLEX NOISE GENERATOR ---
// Generates seamless procedural continents directly on spherical coordinates
const SimplexNoise = (function() {
const F3 = 1.0 / 3.0, G3 = 1.0 / 6.0;
const p = new Uint8Array(256);
for (let i = 0; i < 256; i++) p[i] = Math.floor(Math.random() * 256);
const perm = new Uint8Array(512), permMod12 = new Uint8Array(512);
for (let i = 0; i < 512; i++) {
perm[i] = p[i & 255];
permMod12[i] = (perm[i] % 12);
}
const grad3 = new Float32Array([
1,1,0, -1,1,0, 1,-1,0, -1,-1,0,
1,0,1, -1,0,1, 1,0,-1, -1,0,-1,
0,1,1, 0,-1,1, 0,1,-1, 0,-1,-1
]);
return {
noise: function(xin, yin, zin) {
let n0, n1, n2, n3;
const s = (xin + yin + zin) * F3;
const i = Math.floor(xin + s), j = Math.floor(yin + s), k = Math.floor(zin + s);
const t = (i + j + k) * G3;
const X0 = i - t, Y0 = j - t, Z0 = k - t;
const x0 = xin - X0, y0 = yin - Y0, z0 = zin - Z0;
let i1, j1, k1, i2, j2, k2;
if (x0 >= y0) {
if (y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; }
else if (x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; }
else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }
} else {
if (y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; }
else if (x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; }
else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; }
}
const x1 = x0 - i1 + G3, y1 = y0 - j1 + G3, z1 = z0 - k1 + G3;
const x2 = x0 - i2 + 2.0 * G3, y2 = y0 - j2 + 2.0 * G3, z2 = z0 - k2 + 2.0 * G3;
const x3 = x0 - 1.0 + 3.0 * G3, y3 = y0 - 1.0 + 3.0 * G3, z3 = z0 - 1.0 + 3.0 * G3;
const ii = i & 255, jj = j & 255, kk = k & 255;
let t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
if (t0 < 0) n0 = 0.0;
else {
const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;
t0 *= t0;
n0 = t0 * t0 * (grad3[gi0]*x0 + grad3[gi0+1]*y0 + grad3[gi0+2]*z0);
}
let t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
if (t1 < 0) n1 = 0.0;
else {
const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;
t1 *= t1;
n1 = t1 * t1 * (grad3[gi1]*x1 + grad3[gi1+1]*y1 + grad3[gi1+2]*z1);
}
let t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
if (t2 < 0) n2 = 0.0;
else {
const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;
t2 *= t2;
n2 = t2 * t2 * (grad3[gi2]*x2 + grad3[gi2+1]*y2 + grad3[gi2+2]*z2);
}
let t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
if (t3 < 0) n3 = 0.0;
else {
const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;
t3 *= t3;
n3 = t3 * t3 * (grad3[gi3]*x3 + grad3[gi3+1]*y3 + grad3[gi3+2]*z3);
}
return 32.0 * (n0 + n1 + n2 + n3);
}
};
})();
// Fractal Brownian Motion
function fbm(x, y, z, octaves = 5) {
let val = 0, amp = 0.5, freq = 1.0;
for (let i = 0; i < octaves; i++) {
val += amp * SimplexNoise.noise(x * freq, y * freq, z * freq);
freq *= 2.0;
amp *= 0.5;
}
return val;
}
// --- PROCEDURAL TEXTURE GENERATION ---
function createGlobeTexture() {
const width = 2048, height = 1024;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(width, height);
const data = imgData.data;
for (let y = 0; y < height; y++) {
const lat = ((y / height) - 0.5) * Math.PI; // -PI/2 to PI/2
const sinLat = Math.sin(lat);
const cosLat = Math.cos(lat);
const absLat = Math.abs(lat) / (Math.PI / 2); // 0 at equator, 1 at poles
for (let x = 0; x < width; x++) {
const lon = (x / width) * Math.PI * 2; // 0 to 2PI
const nx = cosLat * Math.sin(lon);
const ny = sinLat;
const nz = cosLat * Math.cos(lon);
// Generate spherical elevation
let n = fbm(nx * 1.5, ny * 1.5, nz * 1.5, 6);
let elevation = (n + 1) * 0.5; // 0.0 to 1.0
// Polar ice caps
let iceFactor = Math.pow(absLat, 4) * 1.6;
let isIce = (elevation + iceFactor) > 0.85;
let r, g, b;
if (isIce && absLat > 0.65) {
// Ice caps
r = 230 + Math.random() * 25;
g = 240 + Math.random() * 15;
b = 255;
} else if (elevation < 0.48) {
// Deep Ocean
r = 5; g = 18; b = 48;
} else if (elevation < 0.52) {
// Shallow Coast / Shelf
r = 15; g = 65; b = 95;
} else if (elevation < 0.54) {
// Sand / Beach
r = 185; g = 170; b = 120;
} else if (elevation < 0.68) {
// Lowlands / Forests
r = 28; g = 95; b = 48;
} else if (elevation < 0.80) {
// Highlands / Hills
r = 85; g = 72; b = 50;
} else {
// Mountains / Peaks
r = 180; g = 180; b = 190;
}
// Night lights / city emission subtle boost
const idx = (y * width + x) * 4;
data[idx] = r;
data[idx + 1] = g;
data[idx + 2] = b;
data[idx + 3] = 255;
}
}
ctx.putImageData(imgData, 0, 0);
return new THREE.CanvasTexture(canvas);
}
function createCloudTexture() {
const width = 1024, height = 512;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(width, height);
const data = imgData.data;
for (let y = 0; y < height; y++) {
const lat = ((y / height) - 0.5) * Math.PI;
const sinLat = Math.sin(lat);
const cosLat = Math.cos(lat);
for (let x = 0; x < width; x++) {
const lon = (x / width) * Math.PI * 2;
const nx = cosLat * Math.sin(lon);
const ny = sinLat;
const nz = cosLat * Math.cos(lon);
let cloud = fbm(nx * 2.2 + 10.0, ny * 2.2 + 10.0, nz * 2.2 + 10.0, 5);
cloud = (cloud + 1) * 0.5;
let density = Math.max(0, (cloud - 0.48) / 0.52);
const idx = (y * width + x) * 4;
data[idx] = 255;
data[idx + 1] = 255;
data[idx + 2] = 255;
data[idx + 3] = Math.floor(density * 220);
}
}
ctx.putImageData(imgData, 0, 0);
return new THREE.CanvasTexture(canvas);
}
// --- SCENE SETUP ---
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 4, 11);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
container.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 5.5;
controls.maxDistance = 25;
controls.rotateSpeed = 0.8;
// --- LIGHTING ---
const sunLight = new THREE.DirectionalLight(0xfff5e6, 2.0);
sunLight.position.set(20, 10, 15);
scene.add(sunLight);
const ambientLight = new THREE.AmbientLight(0x0d1b2a, 0.8);
scene.add(ambientLight);
const blueBackLight = new THREE.DirectionalLight(0x1d4ed8, 1.2);
blueBackLight.position.set(-15, -10, -15);
scene.add(blueBackLight);
// --- GLOBE ROOT GROUP ---
const globeGroup = new THREE.Group();
scene.add(globeGroup);
// 1. Earth Mesh
const earthRadius = 3.0;
const earthGeo = new THREE.SphereGeometry(earthRadius, 64, 64);
const earthTex = createGlobeTexture();
const earthMat = new THREE.MeshStandardMaterial({
map: earthTex,
roughness: 0.7,
metalness: 0.1,
});
const earthMesh = new THREE.Mesh(earthGeo, earthMat);
globeGroup.add(earthMesh);
// 2. Cloud Mesh
const cloudTex = createCloudTexture();
const cloudGeo = new THREE.SphereGeometry(earthRadius + 0.04, 64, 64);
const cloudMat = new THREE.MeshStandardMaterial({
map: cloudTex,
transparent: true,
opacity: 0.75,
blending: THREE.NormalBlending,
depthWrite: false,
roughness: 1.0
});
const cloudMesh = new THREE.Mesh(cloudGeo, cloudMat);
globeGroup.add(cloudMesh);
// 3. Atmosphere Glow Mesh (Custom Fresnel Shader)
const atmosphereVertexShader = `
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vNormal = normalize(normalMatrix * normal);
vPosition = (modelViewMatrix * vec4(position, 1.0)).xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const atmosphereFragmentShader = `
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vec3 viewDir = normalize(-vPosition);
float intensity = pow(1.0 - max(0.0, dot(vNormal, viewDir)), 3.2);
vec3 glowColor = vec3(0.18, 0.58, 1.0);
gl_FragColor = vec4(glowColor, intensity * 0.85);
}
`;
const atmosGeo = new THREE.SphereGeometry(earthRadius + 0.35, 64, 64);
const atmosMat = new THREE.ShaderMaterial({
vertexShader: atmosphereVertexShader,
fragmentShader: atmosphereFragmentShader,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
transparent: true,
depthWrite: false
});
const atmosMesh = new THREE.Mesh(atmosGeo, atmosMat);
globeGroup.add(atmosMesh);
// 4. Orbiting Particle Rings
function createGlowSprite() {
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');
const grad = ctx.createRadialGradient(32, 32, 0, 32, 32, 32);
grad.addColorStop(0, 'rgba(255, 255, 255, 1)');
grad.addColorStop(0.2, 'rgba(0, 242, 254, 0.8)');
grad.addColorStop(0.6, 'rgba(79, 70, 229, 0.3)');
grad.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 64, 64);
return new THREE.CanvasTexture(canvas);
}
const particleCount = 1800;
const particleGeo = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const color1 = new THREE.Color(0x00f2fe);
const color2 = new THREE.Color(0x9d4edd);
for (let i = 0; i < particleCount; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 4.2 + (Math.random() - 0.5) * 1.6;
const spreadY = (Math.random() - 0.5) * 0.45;
positions[i * 3] = Math.cos(angle) * radius;
positions[i * 3 + 1] = spreadY;
positions[i * 3 + 2] = Math.sin(angle) * radius;
const mixedColor = color1.clone().lerp(color2, Math.random());
colors[i * 3] = mixedColor.r;
colors[i * 3 + 1] = mixedColor.g;
colors[i * 3 + 2] = mixedColor.b;
}
particleGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particleGeo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const particleMat = new THREE.PointsMaterial({
size: 0.16,
map: createGlowSprite(),
vertexColors: true,
transparent: true,
blending: THREE.AdditiveBlending,
depthWrite: false
});
const particleRing = new THREE.Points(particleGeo, particleMat);
particleRing.rotation.x = THREE.MathUtils.degToRad(35);
particleRing.rotation.z = THREE.MathUtils.degToRad(15);
scene.add(particleRing);
// 5. Starfield Background
const starsGeo = new THREE.BufferGeometry();
const starsCount = 1200;
const starPositions = new Float32Array(starsCount * 3);
for (let i = 0; i < starsCount * 3; i += 3) {
starPositions[i] = (Math.random() - 0.5) * 200;
starPositions[i + 1] = (Math.random() - 0.5) * 200;
starPositions[i + 2] = (Math.random() - 0.5) * 200;
}
starsGeo.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));
const starsMat = new THREE.PointsMaterial({
size: 0.4,
color: 0x88aaff,
transparent: true,
opacity: 0.6
});
const starField = new THREE.Points(starsGeo, starsMat);
scene.add(starField);
// --- INTERACTIVITY & UI ---
const slider = document.getElementById('rotation-slider');
const speedDisplay = document.getElementById('speed-display');
let speedMultiplier = 1.0;
slider.addEventListener('input', (e) => {
speedMultiplier = parseFloat(e.target.value);
speedDisplay.textContent = speedMultiplier.toFixed(1) + 'x';
});
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// --- ANIMATION LOOP ---
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const baseRot = 0.15 * speedMultiplier * delta;
// Earth and cloud rotation
globeGroup.rotation.y += baseRot;
cloudMesh.rotation.y += baseRot * 0.35; // Cloud differential drift
// Ring rotation (inclined orbit counter-rotation)
particleRing.rotation.y -= baseRot * 0.7;
// Controls update
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
Test 3: 3D Low-Poly Wizard Adventure Game
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arcane Isles - 3D Wizard Adventure</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
-webkit-user-select: none;
}
body {
width: 100vw;
height: 100vh;
overflow: hidden;
background-color: #a0d8ef;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #fff;
}
#game-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: block;
}
/* Stylized UI HUD */
#hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
padding: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.hud-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.hud-card {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 2px solid rgba(255, 255, 255, 0.4);
border-radius: 18px;
padding: 12px 20px;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.15);
display: flex;
align-items: center;
gap: 14px;
}
/* Health Bar */
.health-container {
display: flex;
flex-direction: column;
gap: 6px;
}
.health-label {
font-size: 12px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 1.5px;
color: #fff;
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.health-bar-bg {
width: 180px;
height: 16px;
background: rgba(0, 0, 0, 0.25);
border-radius: 10px;
overflow: hidden;
border: 1.5px solid rgba(255, 255, 255, 0.5);
position: relative;
}
.health-bar-fill {
width: 100%;
height: 100%;
background: linear-gradient(90deg, #ff6b81, #ff4757);
border-radius: 8px;
transition: width 0.25s ease-out, background 0.3s;
}
/* Score / Crystal Counter */
.crystal-counter {
font-size: 20px;
font-weight: 900;
display: flex;
align-items: center;
gap: 8px;
color: #ffeaa7;
text-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.crystal-icon {
width: 22px;
height: 22px;
background: #fdcb6e;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
box-shadow: 0 0 12px #ffeaa7;
animation: pulse-gem 2s infinite ease-in-out;
}
@keyframes pulse-gem {
0%, 100% { transform: scale(1) rotate(0deg); }
50% { transform: scale(1.18) rotate(15deg); }
}
/* Time of Day Indicator */
.time-pill {
font-size: 13px;
font-weight: 700;
color: #fff;
letter-spacing: 1px;
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
/* Controls Hint & Notifications */
.hud-bottom {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.controls-hint {
background: rgba(20, 20, 35, 0.3);
backdrop-filter: blur(8px);
border-radius: 12px;
padding: 8px 16px;
font-size: 12px;
color: rgba(255,255,255,0.85);
display: flex;
gap: 12px;
border: 1px solid rgba(255,255,255,0.2);
}
.key-badge {
background: rgba(255,255,255,0.3);
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
color: #fff;
}
.notification {
position: absolute;
top: 100px;
left: 50%;
transform: translateX(-50%) translateY(-20px);
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(16px);
border: 2px solid rgba(255, 255, 255, 0.6);
padding: 10px 24px;
border-radius: 30px;
font-weight: 800;
font-size: 16px;
letter-spacing: 1px;
text-shadow: 0 2px 6px rgba(0,0,0,0.3);
opacity: 0;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
pointer-events: none;
}
.notification.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* Damage Flash Overlay */
#damage-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle, transparent 60%, rgba(255, 50, 50, 0.45) 100%);
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease-out;
}
/* Dialog & Modal Overlays */
.modal-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(20, 15, 35, 0.65);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
z-index: 100;
opacity: 1;
transition: opacity 0.4s ease;
}
.modal-content {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.08));
border: 2px solid rgba(255, 255, 255, 0.5);
border-radius: 28px;
padding: 40px 50px;
text-align: center;
max-width: 460px;
box-shadow: 0 20px 50px rgba(0,0,0,0.4);
animation: modal-pop 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes modal-pop {
0% { transform: scale(0.85); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
.modal-title {
font-size: 34px;
font-weight: 900;
margin-bottom: 12px;
text-transform: uppercase;
letter-spacing: 2px;
text-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.modal-desc {
font-size: 15px;
line-height: 1.6;
margin-bottom: 28px;
color: rgba(255, 255, 255, 0.9);
}
.btn-action {
background: linear-gradient(135deg, #a29bfe, #6c5ce7);
color: #fff;
border: none;
padding: 14px 36px;
font-size: 16px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
border-radius: 30px;
cursor: pointer;
box-shadow: 0 8px 24px rgba(108, 92, 231, 0.45);
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
outline: none;
}
.btn-action:hover {
transform: translateY(-3px) scale(1.03);
box-shadow: 0 12px 30px rgba(108, 92, 231, 0.65);
background: linear-gradient(135deg, #b3acff, #7d6dfa);
}
.btn-action:active {
transform: translateY(1px);
}
.hidden {
display: none !important;
}
</style>
<!-- Three.js CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<canvas id="game-canvas"></canvas>
<div id="damage-overlay"></div>
<div id="hud">
<div class="hud-top">
<div class="hud-card">
<div class="health-container">
<div class="health-label">Arcane Vitality</div>
<div class="health-bar-bg">
<div id="health-bar" class="health-bar-fill"></div>
</div>
</div>
</div>
<div class="hud-card">
<div class="crystal-counter">
<div class="crystal-icon"></div>
<span id="crystal-text">0 / 8</span>
</div>
</div>
<div class="hud-card">
<div class="time-pill" id="time-pill">☀️ Morning</div>
</div>
</div>
<div id="notification-box" class="notification">Artifact Recovered!</div>
<div class="hud-bottom">
<div class="controls-hint">
<span><span class="key-badge">W A S D</span> Move</span>
<span><span class="key-badge">SPACE</span> Jump</span>
<span><span class="key-badge">Left Click + Drag</span> Rotate Cam</span>
<span><span class="key-badge">Scroll</span> Zoom</span>
</div>
</div>
</div>
<!-- Start / Intro Screen -->
<div id="start-modal" class="modal-screen">
<div class="modal-content">
<h1 class="modal-title" style="color: #ffeaa7;">Arcane Isles</h1>
<p class="modal-desc">
Welcome, Novice Wizard! Explore the floating archipelago, dodge the automated arcane turrets, and gather all <strong>8 Glowing Crystals</strong> to break the celestial seal.
</p>
<button class="btn-action" id="start-btn">Begin Journey</button>
</div>
</div>
<!-- Victory Modal -->
<div id="victory-modal" class="modal-screen hidden">
<div class="modal-content">
<h1 class="modal-title" style="color: #55efc4;">Archipelago Cleansed!</h1>
<p class="modal-desc">
Magnificent! You've restored all the elemental crystals and quelled the arcane storms across the islands.
</p>
<button class="btn-action" id="win-restart-btn">Play Again</button>
</div>
</div>
<!-- Game Over Modal -->
<div id="gameover-modal" class="modal-screen hidden">
<div class="modal-content">
<h1 class="modal-title" style="color: #ff7675;">Mana Depleted</h1>
<p class="modal-desc">
The magical defenses overwhelmed your shield. Revitalize your essence and try again!
</p>
<button class="btn-action" id="lose-restart-btn">Try Again</button>
</div>
</div>
<script>
/* --- AUDIO SYNTHESIZER (Web Audio API) --- */
class SoundFX {
constructor() {
this.ctx = null;
}
init() {
if (!this.ctx) {
const AudioCtx = window.AudioContext || window.webkitAudioContext;
this.ctx = new AudioCtx();
}
if (this.ctx.state === 'suspended') {
this.ctx.resume();
}
}
playJump() {
if (!this.ctx) return;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sine';
const now = this.ctx.currentTime;
osc.frequency.setValueAtTime(150, now);
osc.frequency.exponentialRampToValueAtTime(420, now + 0.18);
gain.gain.setValueAtTime(0.2, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.18);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(now);
osc.stop(now + 0.18);
}
playCollect() {
if (!this.ctx) return;
const notes = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6
notes.forEach((freq, i) => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
const now = this.ctx.currentTime + (i * 0.06);
osc.type = 'triangle';
osc.frequency.setValueAtTime(freq, now);
gain.gain.setValueAtTime(0.22, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.28);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(now);
osc.stop(now + 0.28);
});
}
playHit() {
if (!this.ctx) return;
const now = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(140, now);
osc.frequency.exponentialRampToValueAtTime(35, now + 0.25);
gain.gain.setValueAtTime(0.3, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.25);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(now);
osc.stop(now + 0.25);
}
playTurretShot() {
if (!this.ctx) return;
const now = this.ctx.currentTime;
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(680, now);
osc.frequency.exponentialRampToValueAtTime(180, now + 0.22);
gain.gain.setValueAtTime(0.18, now);
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.22);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(now);
osc.stop(now + 0.22);
}
playWin() {
if (!this.ctx) return;
const melody = [440, 554.37, 659.25, 880];
melody.forEach((freq, index) => {
const osc = this.ctx.createOscillator();
const gain = this.ctx.createGain();
const now = this.ctx.currentTime + index * 0.12;
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, now);
gain.gain.setValueAtTime(0.25, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.5);
osc.connect(gain);
gain.connect(this.ctx.destination);
osc.start(now);
osc.stop(now + 0.5);
});
}
}
const sound = new SoundFX();
/* --- GAME ENGINE & SETUP --- */
const canvas = document.getElementById('game-canvas');
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: false, powerPreference: "high-performance" });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);
// Day-Night Lighting Components
const hemiLight = new THREE.HemisphereLight(0xe0f7fa, 0x5d4037, 0.65);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xfff5e6, 1.2);
dirLight.position.set(40, 60, 30);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 200;
const d = 50;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.bias = -0.0005;
scene.add(dirLight);
// Sky & Fog
const fogColor = new THREE.Color(0xb8e2f2);
scene.background = fogColor;
scene.fog = new THREE.FogExp2(fogColor, 0.012);
/* --- PROCEDURAL WORLD BUILDER --- */
const colliders = [];
const collectibles = [];
const turrets = [];
const particles = [];
const pastelPalette = {
grass: [0x81ecec, 0x74b9ff, 0x55efc4, 0xa8e6cf],
rock: [0x636e72, 0xb2bec3, 0xa29bfe, 0x8190a5],
wood: 0xb38968,
leaves: [0xff9ff3, 0xfeca57, 0x54a0ff, 0x1dd1a1],
crystal: [0xfd79a8, 0x00cec9, 0xffbe76, 0xa29bfe]
};
// Island Generator
function createFloatingIsland(x, y, z, radius, depth, colorIndex = 0) {
const islandGroup = new THREE.Group();
islandGroup.position.set(x, y, z);
// Island Top (Cylinder with taper)
const topGeo = new THREE.CylinderGeometry(radius, radius * 0.85, 3, 9);
const topMat = new THREE.MeshLambertMaterial({
color: pastelPalette.grass[colorIndex % pastelPalette.grass.length],
flatShading: true
});
const topMesh = new THREE.Mesh(topGeo, topMat);
topMesh.position.y = 0;
topMesh.receiveShadow = true;
topMesh.castShadow = true;
islandGroup.add(topMesh);
// Island Bottom Cone / Rocks
const botGeo = new THREE.ConeGeometry(radius * 0.85, depth, 8);
const botMat = new THREE.MeshLambertMaterial({
color: pastelPalette.rock[colorIndex % pastelPalette.rock.length],
flatShading: true
});
const botMesh = new THREE.Mesh(botGeo, botMat);
botMesh.rotation.x = Math.PI;
botMesh.position.y = -depth / 2 - 1.5;
botMesh.receiveShadow = true;
islandGroup.add(botMesh);
// Hanging Mini Rocks / Crystals beneath
for (let i = 0; i < 3; i++) {
const subGeo = new THREE.DodecahedronGeometry(radius * 0.15);
const subMesh = new THREE.Mesh(subGeo, botMat);
const angle = (i / 3) * Math.PI * 2 + Math.random();
const r = radius * 0.45;
subMesh.position.set(Math.cos(angle) * r, -depth * 0.6 - Math.random() * 2, Math.sin(angle) * r);
islandGroup.add(subMesh);
}
scene.add(islandGroup);
// Store collider data (cylinder bounds)
const col = {
type: 'cylinder',
center: new THREE.Vector3(x, y + 1.5, z),
radius: radius,
topY: y + 1.5,
botY: y - 2.0
};
colliders.push(col);
return islandGroup;
}
// Bridge Generator
function createPlankBridge(p1, p2, count = 7) {
const bridgeMat = new THREE.MeshLambertMaterial({ color: pastelPalette.wood, flatShading: true });
for (let i = 0; i <= count; i++) {
const t = i / count;
const pos = new THREE.Vector3().lerpVectors(p1, p2, t);
pos.y -= Math.sin(t * Math.PI) * 1.2; // Natural sag
const plankGeo = new THREE.BoxGeometry(2.6, 0.35, 1.2);
const plank = new THREE.Mesh(plankGeo, bridgeMat);
plank.position.copy(pos);
plank.lookAt(p2);
plank.receiveShadow = true;
plank.castShadow = true;
scene.add(plank);
colliders.push({
type: 'box',
center: pos.clone(),
radius: 1.8,
topY: pos.y + 0.3
});
}
}
// Tree Generator
function createLowPolyTree(x, y, z, color = pastelPalette.leaves[0], scale = 1) {
const treeGroup = new THREE.Group();
treeGroup.position.set(x, y, z);
treeGroup.scale.set(scale, scale, scale);
// Trunk
const trunkGeo = new THREE.CylinderGeometry(0.3, 0.45, 2.2, 5);
const trunkMat = new THREE.MeshLambertMaterial({ color: 0x8b5a2b, flatShading: true });
const trunk = new THREE.Mesh(trunkGeo, trunkMat);
trunk.position.y = 1.1;
trunk.castShadow = true;
treeGroup.add(trunk);
// Foliage Cone Layers
const leavesMat = new THREE.MeshLambertMaterial({ color: color, flatShading: true });
for (let i = 0; i < 3; i++) {
const foliageGeo = new THREE.ConeGeometry(1.8 - i * 0.38, 2.2 - i * 0.25, 6);
const foliage = new THREE.Mesh(foliageGeo, leavesMat);
foliage.position.y = 2.2 + i * 1.2;
foliage.castShadow = true;
foliage.receiveShadow = true;
treeGroup.add(foliage);
}
scene.add(treeGroup);
}
// Glowing Crystal Artifact Generator
function createArtifact(x, y, z, colorHex) {
const group = new THREE.Group();
group.position.set(x, y, z);
const gemGeo = new THREE.OctahedronGeometry(0.85, 0);
const gemMat = new THREE.MeshStandardMaterial({
color: colorHex,
emissive: colorHex,
emissiveIntensity: 0.7,
roughness: 0.2,
metalness: 0.8,
flatShading: true
});
const gemMesh = new THREE.Mesh(gemGeo, gemMat);
gemMesh.castShadow = true;
group.add(gemMesh);
// Small orbital ring
const ringGeo = new THREE.TorusGeometry(1.3, 0.06, 6, 16);
const ringMat = new THREE.MeshBasicMaterial({ color: 0xffffff, wireframe: true });
const ringMesh = new THREE.Mesh(ringGeo, ringMat);
ringMesh.rotation.x = Math.PI / 3;
group.add(ringMesh);
// Point Light for radiance
const light = new THREE.PointLight(colorHex, 1.8, 9);
group.add(light);
scene.add(group);
const item = {
group: group,
mesh: gemMesh,
ring: ringMesh,
baseY: y,
collected: false,
pos: new THREE.Vector3(x, y, z)
};
collectibles.push(item);
return item;
}
// Elemental Turret Generator
function createTurret(x, y, z) {
const turretGroup = new THREE.Group();
turretGroup.position.set(x, y, z);
// Base pedastal
const baseGeo = new THREE.CylinderGeometry(1.4, 1.8, 1.2, 6);
const baseMat = new THREE.MeshLambertMaterial({ color: 0x485460, flatShading: true });
const baseMesh = new THREE.Mesh(baseGeo, baseMat);
baseMesh.position.y = 0.6;
baseMesh.castShadow = true;
turretGroup.add(baseMesh);
// Floating Floating Obelisk Core
const coreGeo = new THREE.OctahedronGeometry(1.1, 0);
const coreMat = new THREE.MeshStandardMaterial({
color: 0xff3838,
emissive: 0xff3838,
emissiveIntensity: 0.6,
roughness: 0.3,
flatShading: true
});
const coreMesh = new THREE.Mesh(coreGeo, coreMat);
coreMesh.position.y = 2.8;
coreMesh.castShadow = true;
turretGroup.add(coreMesh);
scene.add(turretGroup);
const turretObj = {
group: turretGroup,
core: coreMesh,
pos: new THREE.Vector3(x, y + 2.8, z),
fireTimer: Math.random() * 2.0,
fireInterval: 2.8,
range: 35
};
turrets.push(turretObj);
}
// Projectiles List
const projectiles = [];
function spawnTurretProjectile(origin, targetPos) {
const dir = new THREE.Vector3().subVectors(targetPos, origin).normalize();
const projGeo = new THREE.SphereGeometry(0.4, 8, 8);
const projMat = new THREE.MeshBasicMaterial({ color: 0xff4757 });
const projMesh = new THREE.Mesh(projGeo, projMat);
projMesh.position.copy(origin);
const pLight = new THREE.PointLight(0xff4757, 2, 8);
projMesh.add(pLight);
scene.add(projMesh);
projectiles.push({
mesh: projMesh,
dir: dir,
speed: 16,
life: 3.5
});
sound.playTurretShot();
}
/* --- BUILD SCENE ENVIRONMENT --- */
// Main Archipelago Islands
const islandsConfig = [
{ x: 0, y: 0, z: 0, r: 14, d: 16, col: 0 }, // 1. Central Spawn
{ x: 26, y: 3, z: -18, r: 10, d: 13, col: 1 }, // 2. North-East Ridge
{ x: -28, y: 4, z: -20, r: 11, d: 14, col: 2 }, // 3. North-West Mystic
{ x: 34, y: 8, z: 22, r: 12, d: 15, col: 3 }, // 4. South-East Summit
{ x: -30, y: 6, z: 24, r: 10, d: 12, col: 0 }, // 5. South-West Spire
{ x: 0, y: 12, z: -48, r: 13, d: 18, col: 1 }, // 6. Northern High Sanctum
{ x: 0, y: 15, z: 50, r: 12, d: 16, col: 2 }, // 7. Southern Arch Island
];
islandsConfig.forEach(cfg => {
createFloatingIsland(cfg.x, cfg.y, cfg.z, cfg.r, cfg.d, cfg.col);
});
// Bridges between islands
createPlankBridge(new THREE.Vector3(0, 1.5, 0), new THREE.Vector3(26, 4.5, -18), 8);
createPlankBridge(new THREE.Vector3(0, 1.5, 0), new THREE.Vector3(-28, 5.5, -20), 8);
createPlankBridge(new THREE.Vector3(26, 4.5, -18), new THREE.Vector3(34, 9.5, 22), 9);
createPlankBridge(new THREE.Vector3(-28, 5.5, -20), new THREE.Vector3(-30, 7.5, 24), 9);
createPlankBridge(new THREE.Vector3(26, 4.5, -18), new THREE.Vector3(0, 13.5, -48), 10);
createPlankBridge(new THREE.Vector3(-28, 5.5, -20), new THREE.Vector3(0, 13.5, -48), 10);
createPlankBridge(new THREE.Vector3(34, 9.5, 22), new THREE.Vector3(0, 16.5, 50), 9);
createPlankBridge(new THREE.Vector3(-30, 7.5, 24), new THREE.Vector3(0, 16.5, 50), 9);
// Decorate Islands with Trees & Vegetation
// Spawn
createLowPolyTree(-4, 1.5, -4, pastelPalette.leaves[0], 1.2);
createLowPolyTree(5, 1.5, -5, pastelPalette.leaves[1], 1.0);
createLowPolyTree(-6, 1.5, 4, pastelPalette.leaves[2], 0.9);
// Island 2
createLowPolyTree(28, 4.5, -22, pastelPalette.leaves[3], 1.3);
createLowPolyTree(22, 4.5, -14, pastelPalette.leaves[0], 0.8);
// Island 3
createLowPolyTree(-32, 5.5, -24, pastelPalette.leaves[1], 1.1);
createLowPolyTree(-24, 5.5, -16, pastelPalette.leaves[2], 1.0);
// Island 4 & 5
createLowPolyTree(38, 9.5, 26, pastelPalette.leaves[0], 1.4);
createLowPolyTree(-34, 7.5, 28, pastelPalette.leaves[3], 1.2);
// High Sanctum & South Arch
createLowPolyTree(-5, 13.5, -52, pastelPalette.leaves[1], 1.5);
createLowPolyTree(6, 13.5, -45, pastelPalette.leaves[2], 1.3);
createLowPolyTree(0, 16.5, 54, pastelPalette.leaves[0], 1.5);
// Distribute 8 Crystals
const crystalPositions = [
{ x: 0, y: 3.2, z: 6, col: 0x55efc4 }, // Spawn intro
{ x: 28, y: 6.2, z: -18, col: 0xfd79a8 }, // Island 2
{ x: -30, y: 7.2, z: -20, col: 0xffbe76 }, // Island 3
{ x: 36, y: 11.2, z: 22, col: 0x00cec9 }, // Island 4
{ x: -32, y: 9.2, z: 24, col: 0xa29bfe }, // Island 5
{ x: -6, y: 15.2, z: -48, col: 0xfdcb6e }, // Island 6 Left
{ x: 6, y: 15.2, z: -48, col: 0xe056fd }, // Island 6 Right
{ x: 0, y: 18.2, z: 50, col: 0x7ed6df } // Island 7 Summit
];
crystalPositions.forEach(cp => {
createArtifact(cp.x, cp.y, cp.z, cp.col);
});
// Elemental Turrets (Strategically placed)
createTurret(24, 4.5, -24);
createTurret(-26, 5.5, -26);
createTurret(32, 9.5, 16);
createTurret(0, 13.5, -42);
createTurret(0, 16.5, 44);
/* --- AMBIENT MAGIC PARTICLES --- */
const moteCount = 120;
const moteGeo = new THREE.BufferGeometry();
const motePos = new Float32Array(moteCount * 3);
for (let i = 0; i < moteCount * 3; i += 3) {
motePos[i] = (Math.random() - 0.5) * 120;
motePos[i + 1] = Math.random() * 25 + 2;
motePos[i + 2] = (Math.random() - 0.5) * 120;
}
moteGeo.setAttribute('position', new THREE.BufferAttribute(motePos, 3));
const moteMat = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.6,
transparent: true,
opacity: 0.65,
blending: THREE.AdditiveBlending
});
const moteSystem = new THREE.Points(moteGeo, moteMat);
scene.add(moteSystem);
/* --- LOW-POLY WIZARD CHARACTER --- */
class WizardCharacter {
constructor() {
this.mesh = new THREE.Group();
// Materials
const robeMat = new THREE.MeshLambertMaterial({ color: 0x4834d4, flatShading: true });
const skinMat = new THREE.MeshLambertMaterial({ color: 0xffd1a4, flatShading: true });
const hatMat = new THREE.MeshLambertMaterial({ color: 0x301b96, flatShading: true });
const trimMat = new THREE.MeshLambertMaterial({ color: 0xf9ca24, flatShading: true });
const beardMat = new THREE.MeshLambertMaterial({ color: 0xecf0f1, flatShading: true });
const woodMat = new THREE.MeshLambertMaterial({ color: 0x795548, flatShading: true });
// Body / Robe Cone
const bodyGeo = new THREE.ConeGeometry(0.7, 1.6, 7);
this.body = new THREE.Mesh(bodyGeo, robeMat);
this.body.position.y = 0.8;
this.body.castShadow = true;
this.mesh.add(this.body);
// Belt Trim
const beltGeo = new THREE.CylinderGeometry(0.52, 0.56, 0.18, 7);
const belt = new THREE.Mesh(beltGeo, trimMat);
belt.position.y = 0.75;
this.mesh.add(belt);
// Head
const headGeo = new THREE.SphereGeometry(0.38, 6, 6);
this.head = new THREE.Mesh(headGeo, skinMat);
this.head.position.y = 1.85;
this.head.castShadow = true;
this.mesh.add(this.head);
// Beard
const beardGeo = new THREE.ConeGeometry(0.28, 0.65, 5);
const beard = new THREE.Mesh(beardGeo, beardMat);
beard.rotation.x = -Math.PI * 0.15;
beard.position.set(0, 1.62, 0.28);
this.mesh.add(beard);
// Wizard Hat Brim & Cone
const brimGeo = new THREE.CylinderGeometry(0.9, 0.9, 0.08, 8);
const brim = new THREE.Mesh(brimGeo, hatMat);
brim.position.y = 2.05;
this.mesh.add(brim);
const hatConeGeo = new THREE.ConeGeometry(0.55, 1.2, 7);
this.hatCone = new THREE.Mesh(hatConeGeo, hatMat);
this.hatCone.position.set(0, 2.65, -0.08);
this.hatCone.rotation.x = -0.15;
this.mesh.add(this.hatCone);
// Magic Staff
this.staffGroup = new THREE.Group();
this.staffGroup.position.set(0.65, 0.9, 0.35);
const poleGeo = new THREE.CylinderGeometry(0.06, 0.07, 2.2, 5);
const pole = new THREE.Mesh(poleGeo, woodMat);
pole.castShadow = true;
this.staffGroup.add(pole);
// Staff Crystal Top
const staffGemGeo = new THREE.OctahedronGeometry(0.22, 0);
const staffGemMat = new THREE.MeshStandardMaterial({
color: 0x00d2d3,
emissive: 0x00d2d3,
emissiveIntensity: 0.9,
flatShading: true
});
this.staffGem = new THREE.Mesh(staffGemGeo, staffGemMat);
this.staffGem.position.y = 1.15;
this.staffGroup.add(this.staffGem);
this.staffLight = new THREE.PointLight(0x00d2d3, 1.2, 6);
this.staffLight.position.y = 1.15;
this.staffGroup.add(this.staffLight);
this.mesh.add(this.staffGroup);
scene.add(this.mesh);
// Movement & State Properties
this.position = this.mesh.position;
this.position.set(0, 3, 0);
this.velocity = new THREE.Vector3();
this.isGrounded = false;
this.walkCycle = 0;
this.rotationAngle = 0;
}
updateAnimation(delta, isMoving) {
// Bobbing staff & floating gem rotation
this.staffGem.rotation.y += delta * 3.5;
this.staffGem.rotation.x += delta * 2;
if (isMoving && this.isGrounded) {
this.walkCycle += delta * 12;
this.body.rotation.z = Math.sin(this.walkCycle) * 0.08;
this.staffGroup.position.y = 0.9 + Math.sin(this.walkCycle) * 0.1;
this.hatCone.rotation.z = Math.sin(this.walkCycle) * 0.1;
} else {
this.walkCycle = 0;
this.body.rotation.z = THREE.MathUtils.lerp(this.body.rotation.z, 0, 0.1);
this.hatCone.rotation.z = THREE.MathUtils.lerp(this.hatCone.rotation.z, 0, 0.1);
this.staffGroup.position.y = THREE.MathUtils.lerp(this.staffGroup.position.y, 0.9, 0.1);
}
}
}
const player = new WizardCharacter();
/* --- GAMEPLAY CONTROLLER & INPUTS --- */
const keys = { w: false, a: false, s: false, d: false, space: false };
let mouseLook = { isDragging: false, prevX: 0, prevY: 0, yaw: 0, pitch: 0.35, distance: 11 };
window.addEventListener('keydown', (e) => {
const k = e.key.toLowerCase();
if (k === 'w' || k === 'arrowup') keys.w = true;
if (k === 'a' || k === 'arrowleft') keys.a = true;
if (k === 's' || k === 'arrowdown') keys.s = true;
if (k === 'd' || k === 'arrowright') keys.d = true;
if (e.code === 'Space') {
if (!keys.space && player.isGrounded) {
player.velocity.y = 12.5;
player.isGrounded = false;
sound.playJump();
}
keys.space = true;
}
});
window.addEventListener('keyup', (e) => {
const k = e.key.toLowerCase();
if (k === 'w' || k === 'arrowup') keys.w = false;
if (k === 'a' || k === 'arrowleft') keys.a = false;
if (k === 's' || k === 'arrowdown') keys.s = false;
if (k === 'd' || k === 'arrowright') keys.d = false;
if (e.code === 'Space') keys.space = false;
});
// Mouse Drag Orbit Controls
window.addEventListener('mousedown', (e) => {
if (e.button === 0 || e.button === 2) {
mouseLook.isDragging = true;
mouseLook.prevX = e.clientX;
mouseLook.prevY = e.clientY;
}
});
window.addEventListener('mouseup', () => { mouseLook.isDragging = false; });
window.addEventListener('mousemove', (e) => {
if (!mouseLook.isDragging) return;
const dx = e.clientX - mouseLook.prevX;
const dy = e.clientY - mouseLook.prevY;
mouseLook.prevX = e.clientX;
mouseLook.prevY = e.clientY;
mouseLook.yaw -= dx * 0.007;
mouseLook.pitch = Math.max(0.08, Math.min(Math.PI / 2.3, mouseLook.pitch + dy * 0.005));
});
window.addEventListener('wheel', (e) => {
mouseLook.distance = Math.max(5, Math.min(22, mouseLook.distance + e.deltaY * 0.015));
});
window.addEventListener('contextmenu', e => e.preventDefault());
/* --- GAME STATE & HUD LOGIC --- */
let gameState = {
health: 100,
maxHealth: 100,
crystalsCollected: 0,
totalCrystals: crystalPositions.length,
isGameOver: false,
isWon: false,
dayTime: 0 // 0 to 2PI
};
const healthBar = document.getElementById('health-bar');
const crystalText = document.getElementById('crystal-text');
const timePill = document.getElementById('time-pill');
const notifBox = document.getElementById('notification-box');
const damageOverlay = document.getElementById('damage-overlay');
function triggerDamage(amount = 20) {
if (gameState.isGameOver || gameState.isWon) return;
gameState.health = Math.max(0, gameState.health - amount);
healthBar.style.width = `${(gameState.health / gameState.maxHealth) * 100}%`;
damageOverlay.style.opacity = '1';
setTimeout(() => { damageOverlay.style.opacity = '0'; }, 180);
sound.playHit();
if (gameState.health <= 0) {
handleGameOver();
}
}
function showNotification(msg) {
notifBox.innerText = msg;
notifBox.classList.add('show');
setTimeout(() => { notifBox.classList.remove('show'); }, 2200);
}
function collectCrystal(item) {
item.collected = true;
scene.remove(item.group);
gameState.crystalsCollected++;
crystalText.innerText = `${gameState.crystalsCollected} / ${gameState.totalCrystals}`;
sound.playCollect();
showNotification(`✨ Crystal Found! (${gameState.crystalsCollected}/${gameState.totalCrystals})`);
// Spawn Sparkle Burst
for (let i = 0; i < 16; i++) {
const spGeo = new THREE.DodecahedronGeometry(0.18);
const spMat = new THREE.MeshBasicMaterial({ color: 0xffeaa7 });
const spMesh = new THREE.Mesh(spGeo, spMat);
spMesh.position.copy(item.pos);
scene.add(spMesh);
particles.push({
mesh: spMesh,
vel: new THREE.Vector3((Math.random() - 0.5) * 8, Math.random() * 8 + 2, (Math.random() - 0.5) * 8),
life: 0.8
});
}
if (gameState.crystalsCollected >= gameState.totalCrystals) {
handleVictory();
}
}
function handleGameOver() {
gameState.isGameOver = true;
document.getElementById('gameover-modal').classList.remove('hidden');
}
function handleVictory() {
gameState.isWon = true;
sound.playWin();
document.getElementById('victory-modal').classList.remove('hidden');
}
function restartGame() {
gameState.health = 100;
gameState.crystalsCollected = 0;
gameState.isGameOver = false;
gameState.isWon = false;
healthBar.style.width = '100%';
crystalText.innerText = `0 / ${gameState.totalCrystals}`;
player.position.set(0, 3, 0);
player.velocity.set(0, 0, 0);
// Re-add collectibles
collectibles.forEach(item => {
item.collected = false;
scene.add(item.group);
});
// Clear projectiles
projectiles.forEach(p => scene.remove(p.mesh));
projectiles.length = 0;
document.getElementById('gameover-modal').classList.add('hidden');
document.getElementById('victory-modal').classList.add('hidden');
document.getElementById('start-modal').classList.add('hidden');
}
document.getElementById('start-btn').addEventListener('click', () => {
sound.init();
document.getElementById('start-modal').classList.add('hidden');
});
document.getElementById('win-restart-btn').addEventListener('click', restartGame);
document.getElementById('lose-restart-btn').addEventListener('click', restartGame);
/* --- COLLISION RESOLUTION --- */
function checkGroundCollision(pos) {
let highestGround = -999;
for (let col of colliders) {
if (col.type === 'cylinder') {
const dx = pos.x - col.center.x;
const dz = pos.z - col.center.z;
const distSq = dx * dx + dz * dz;
if (distSq < col.radius * col.radius) {
if (col.topY > highestGround && pos.y >= col.topY - 1.8) {
highestGround = col.topY;
}
}
} else if (col.type === 'box') {
const distSq = pos.distanceToSquared(col.center);
if (distSq < col.radius * col.radius) {
if (col.topY > highestGround && pos.y >= col.topY - 1.4) {
highestGround = col.topY;
}
}
}
}
return highestGround;
}
/* --- MAIN ANIMATION & PHYSICS LOOP --- */
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = Math.min(clock.getDelta(), 0.08);
if (!gameState.isGameOver && !gameState.isWon) {
// Day-Night Cycle
gameState.dayTime += delta * 0.06;
const sunAngle = gameState.dayTime;
dirLight.position.x = Math.cos(sunAngle) * 60;
dirLight.position.y = Math.sin(sunAngle) * 60;
dirLight.position.z = Math.sin(sunAngle * 0.5) * 40;
// Adjust lighting & fog colors dynamically
const sunY = dirLight.position.y;
if (sunY > 10) {
// Daytime
scene.background.setHex(0xb8e2f2);
scene.fog.color.setHex(0xb8e2f2);
dirLight.color.setHex(0xfff5e6);
hemiLight.color.setHex(0xe0f7fa);
timePill.innerText = "☀️ Midday";
} else if (sunY > -10) {
// Golden Hour / Sunset
scene.background.setHex(0xfbc531);
scene.fog.color.setHex(0xfbc531);
dirLight.color.setHex(0xff7675);
hemiLight.color.setHex(0xfd79a8);
timePill.innerText = "🌅 Twilight";
} else {
// Night
scene.background.setHex(0x192a56);
scene.fog.color.setHex(0x192a56);
dirLight.color.setHex(0x487eb0);
hemiLight.color.setHex(0x273c75);
timePill.innerText = "🌙 Night";
}
// Player Movement Physics
const moveDir = new THREE.Vector3();
if (keys.w) moveDir.z -= 1;
if (keys.s) moveDir.z += 1;
if (keys.a) moveDir.x -= 1;
if (keys.d) moveDir.x += 1;
const isMoving = moveDir.lengthSq() > 0;
if (isMoving) {
moveDir.normalize();
// Camera yaw relative movement
const forward = new THREE.Vector3(0, 0, -1).applyAxisAngle(new THREE.Vector3(0, 1, 0), mouseLook.yaw);
const right = new THREE.Vector3(1, 0, 0).applyAxisAngle(new THREE.Vector3(0, 1, 0), mouseLook.yaw);
const moveVector = new THREE.Vector3()
.addScaledVector(forward, -moveDir.z)
.addScaledVector(right, moveDir.x)
.normalize();
const moveSpeed = 10.5;
player.velocity.x = moveVector.x * moveSpeed;
player.velocity.z = moveVector.z * moveSpeed;
// Smooth rotation towards travel
const targetAngle = Math.atan2(moveVector.x, moveVector.z);
let diff = targetAngle - player.mesh.rotation.y;
while (diff < -Math.PI) diff += Math.PI * 2;
while (diff > Math.PI) diff -= Math.PI * 2;
player.mesh.rotation.y += diff * 0.18;
} else {
player.velocity.x *= 0.78;
player.velocity.z *= 0.78;
}
// Apply Gravity
player.velocity.y -= 28.0 * delta;
// Move Player
player.position.x += player.velocity.x * delta;
player.position.z += player.velocity.z * delta;
player.position.y += player.velocity.y * delta;
// Ground Collision Resolution
const groundY = checkGroundCollision(player.position);
if (player.position.y <= groundY) {
player.position.y = groundY;
player.velocity.y = 0;
player.isGrounded = true;
} else {
player.isGrounded = false;
}
// Void Respawn & Penalty
if (player.position.y < -30) {
player.position.set(0, 4, 0);
player.velocity.set(0, 0, 0);
triggerDamage(25);
showNotification("⚠️ Fallen into the void! Teleported back.");
}
// Update Character Animation
player.updateAnimation(delta, isMoving);
// Collectibles Animation & Hit Check
collectibles.forEach(item => {
if (item.collected) return;
item.mesh.rotation.y += delta * 1.5;
item.ring.rotation.z += delta * 2;
item.group.position.y = item.baseY + Math.sin(clock.elapsedTime * 3 + item.baseY) * 0.35;
// Check Player Proximity
if (player.position.distanceTo(item.group.position) < 2.0) {
collectCrystal(item);
}
});
// Turrets AI & Shooting
turrets.forEach(turret => {
turret.core.rotation.y += delta * 2;
turret.core.position.y = 2.8 + Math.sin(clock.elapsedTime * 2.5) * 0.25;
const distToPlayer = turret.pos.distanceTo(player.position);
if (distToPlayer < turret.range) {
turret.fireTimer += delta;
if (turret.fireTimer >= turret.fireInterval) {
turret.fireTimer = 0;
// Target player center
const target = player.position.clone().add(new THREE.Vector3(0, 1.0, 0));
spawnTurretProjectile(turret.pos, target);
}
}
});
// Projectiles Update & Collision
for (let i = projectiles.length - 1; i >= 0; i--) {
const p = projectiles[i];
p.mesh.position.addScaledVector(p.dir, p.speed * delta);
p.life -= delta;
// Hit Player check
const playerCenter = player.position.clone().add(new THREE.Vector3(0, 1.0, 0));
if (p.mesh.position.distanceTo(playerCenter) < 1.3) {
triggerDamage(18);
scene.remove(p.mesh);
projectiles.splice(i, 1);
continue;
}
// Expired
if (p.life <= 0) {
scene.remove(p.mesh);
projectiles.splice(i, 1);
}
}
// Particles Update
for (let i = particles.length - 1; i >= 0; i--) {
const pt = particles[i];
pt.mesh.position.addScaledVector(pt.vel, delta);
pt.vel.y -= 9.8 * delta;
pt.life -= delta;
pt.mesh.scale.multiplyScalar(0.95);
if (pt.life <= 0) {
scene.remove(pt.mesh);
particles.splice(i, 1);
}
}
// Ambient Motes Drift
const posArr = moteSystem.geometry.attributes.position.array;
for (let i = 1; i < moteCount * 3; i += 3) {
posArr[i] += Math.sin(clock.elapsedTime + i) * 0.02;
}
moteSystem.geometry.attributes.position.needsUpdate = true;
}
// Smooth Third-Person Camera Follow
const camTarget = player.position.clone().add(new THREE.Vector3(0, 1.5, 0));
const camOffsetX = Math.sin(mouseLook.yaw) * Math.cos(mouseLook.pitch) * mouseLook.distance;
const camOffsetY = Math.sin(mouseLook.pitch) * mouseLook.distance;
const camOffsetZ = Math.cos(mouseLook.yaw) * Math.cos(mouseLook.pitch) * mouseLook.distance;
const desiredCamPos = new THREE.Vector3(
camTarget.x + camOffsetX,
camTarget.y + camOffsetY,
camTarget.z + camOffsetZ
);
camera.position.lerp(desiredCamPos, 0.12);
camera.lookAt(camTarget);
renderer.render(scene, camera);
}
// Window Resizing
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Start Loop
animate();
</script>
</body>
</html>
Final Verdict
Read Next
DeepSeek V4.1 Flash Review: Three Hands-On Coding Benchmarks
A hands-on DeepSeek V4.1 Flash review covering its 552B MoE architecture, compressed KV cache, official agent benchmarks, and three interactive creative-coding tests.
Meta's Muse Spark 1.3 Released: 2M Context, Open Weights Roadmap, Benchmarks & Complete Developer Guide
Meta has officially introduced Muse Spark 1.3, doubling the context window to 2M tokens, slashing reasoning latency by 35%, and cutting API pricing to $0.90/$3.20 per million tokens. We analyze its 51.2% SWE-bench Lite score, terminal coding prowess, and Mark Zuckerberg's open-weights release timeline.
Ox Alpha Confirmed as GLM-5.3-Flash: Specs, Pricing Breakdown & Interactive Crane Benchmark
Ox Alpha has officially been confirmed as Zhipu AI's GLM-5.3-Flash. With 320B total/18B active parameters, a hybrid sparse-linear attention architecture, and 50% promotional pricing of $0.075/$0.25 per 1M tokens, we benchmark its coding and SVG generation on Z.ai.