Claude Sonnet 5.5 Browser Coding Check: Glass Crane, Globe & Wizard Game
Three single-file HTML creations, with embedded previews and notes from a quick hands-on browser check.
Co-Founder & Lead Programmer of AcceleratedLogic AI
At a glance
1. Glass sphere and origami crane
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Origami Crane in Glass</title>
<style>
:root { box-sizing: border-box; padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px); --bg: #d9dfe5; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --bg: #1b1f24; } }
:root[data-theme="dark"] { --bg: #1b1f24; }
html, body { height: 100%; margin: 0; }
body { background: var(--bg); display: flex; align-items: center; justify-content: center; }
svg { width: min(100%, 92vh); height: auto; display: block; }
</style>
</head>
<body>
<svg viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="An origami crane inside a photorealistic glass sphere">
<defs>
<linearGradient id="wall" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#c9d3dc"/><stop offset="1" stop-color="#f1f4f6"/></linearGradient>
<linearGradient id="floor" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ddd8d0"/><stop offset="1" stop-color="#9f9a93"/></linearGradient>
<radialGradient id="shadow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#000" stop-opacity=".7"/><stop offset=".6" stop-color="#000" stop-opacity=".3"/><stop offset="1" stop-color="#000" stop-opacity="0"/></radialGradient>
<radialGradient id="caustic" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#fff" stop-opacity=".95"/><stop offset=".5" stop-color="#ffe9c4" stop-opacity=".5"/><stop offset="1" stop-color="#ffe9c4" stop-opacity="0"/></radialGradient>
<radialGradient id="body" cx=".4" cy=".35" r=".75"><stop offset="0" stop-color="#fff" stop-opacity=".3"/><stop offset=".6" stop-color="#bcdcea" stop-opacity=".14"/><stop offset="1" stop-color="#5a8ca0" stop-opacity=".3"/></radialGradient>
<radialGradient id="fres" cx=".5" cy=".5" r=".5"><stop offset=".78" stop-color="#fff" stop-opacity="0"/><stop offset=".9" stop-color="#96bed2" stop-opacity=".2"/><stop offset=".965" stop-color="#28465a" stop-opacity=".5"/><stop offset="1" stop-color="#fff" stop-opacity=".6"/></radialGradient>
<radialGradient id="ringG" cx=".5" cy=".5" r=".5"><stop offset=".55" stop-color="#000"/><stop offset="1" stop-color="#fff"/></radialGradient>
<radialGradient id="glow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#fff" stop-opacity=".7"/><stop offset="1" stop-color="#cfefff" stop-opacity="0"/></radialGradient>
<linearGradient id="hl" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fff" stop-opacity=".95"/><stop offset="1" stop-color="#fff" stop-opacity=".15"/></linearGradient>
<linearGradient id="rimHi" x1="0" y1="1" x2="1" y2="0"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset=".6" stop-color="#fff" stop-opacity=".9"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient>
<linearGradient id="pLight" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ff9a78"/><stop offset="1" stop-color="#f0503a"/></linearGradient>
<linearGradient id="pMid" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#e4382f"/><stop offset="1" stop-color="#b8212a"/></linearGradient>
<linearGradient id="pDark" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#a51b24"/><stop offset="1" stop-color="#6a0f19"/></linearGradient>
<linearGradient id="pBack" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f7f0e6"/><stop offset="1" stop-color="#cfc4b5"/></linearGradient>
<filter id="ds" x="-20%" y="-20%" width="140%" height="150%"><feDropShadow dx="7" dy="11" stdDeviation="6" flood-color="#2a0508" flood-opacity=".4"/></filter>
<filter id="b1"><feGaussianBlur stdDeviation="1.5"/></filter>
<filter id="b3"><feGaussianBlur stdDeviation="3"/></filter>
<filter id="b12" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="12"/></filter>
<filter id="tint"><feColorMatrix values=".2 0 0 0 0 0 .6 0 0 .15 0 0 1 0 .3 0 0 0 1 0"/><feGaussianBlur stdDeviation="1.2"/></filter>
<clipPath id="orb"><circle cx="400" cy="380" r="260"/></clipPath>
<mask id="ring"><circle cx="400" cy="380" r="260" fill="url(#ringG)"/></mask>
<g id="crane" stroke="#5a0a12" stroke-opacity=".4" stroke-width=".8" stroke-linejoin="round">
<!-- far wing (paper underside) -->
<polygon points="388,352 436,176 430,362" fill="url(#pBack)"/>
<polygon points="436,176 462,368 430,362" fill="#c9bdae"/>
<!-- tail -->
<polygon points="258,316 332,400 352,376" fill="url(#pMid)"/>
<polygon points="258,316 352,376 340,362" fill="url(#pDark)"/>
<!-- neck and head -->
<polygon points="466,392 500,318 514,326 480,410" fill="url(#pDark)"/>
<polygon points="500,318 508,296 526,302 558,322 514,328" fill="url(#pLight)"/>
<polygon points="526,302 558,322 514,328" fill="url(#pMid)"/>
<!-- body -->
<polygon points="330,400 400,338 400,452" fill="url(#pLight)"/>
<polygon points="400,338 472,400 400,452" fill="url(#pMid)"/>
<polygon points="330,400 400,452 372,424" fill="url(#pMid)"/>
<polygon points="472,400 400,452 430,428" fill="url(#pDark)"/>
<!-- near wing -->
<polygon points="366,378 320,190 404,368" fill="url(#pLight)"/>
<polygon points="320,190 440,372 404,368" fill="url(#pMid)"/>
<polygon points="320,190 352,262 334,270" fill="url(#pDark)"/>
<g stroke="#fff" stroke-opacity=".35" stroke-width=".9" fill="none">
<path d="M320 190 L402 370"/><path d="M400 338 L400 452"/><path d="M500 318 L466 392"/>
</g>
</g>
</defs>
<!-- studio backdrop -->
<rect width="800" height="560" fill="url(#wall)"/>
<rect y="560" width="800" height="240" fill="url(#floor)"/>
<ellipse cx="400" cy="650" rx="230" ry="34" fill="url(#shadow)" filter="url(#b3)"/>
<ellipse cx="452" cy="672" rx="105" ry="16" fill="url(#caustic)" filter="url(#b3)"/>
<!-- glass sphere -->
<g clip-path="url(#orb)">
<circle cx="400" cy="380" r="260" fill="url(#body)"/>
<!-- inverted, compressed refraction of the room -->
<rect x="140" y="470" width="520" height="170" fill="#f4f6f8" opacity=".22"/>
<rect x="140" y="300" width="520" height="30" fill="#9f9a93" opacity=".08"/>
<g transform="translate(400 385) scale(1.12) translate(-409 -314)" filter="url(#ds)"><use href="#crane"/></g>
<!-- chromatic refraction ghost near the edge -->
<g mask="url(#ring)" style="mix-blend-mode:screen" opacity=".55">
<use href="#crane" filter="url(#tint)" transform="translate(400 380) scale(1.19) translate(-409 -318) rotate(2 409 314)"/>
</g>
<ellipse cx="410" cy="590" rx="180" ry="42" fill="url(#glow)" filter="url(#b12)"/>
<circle cx="400" cy="380" r="260" fill="url(#fres)"/>
</g>
<!-- reflections -->
<ellipse cx="290" cy="215" rx="66" ry="26" transform="rotate(-42 290 215)" fill="url(#hl)" filter="url(#b1)"/>
<g transform="rotate(-42 290 215)" fill="#fff" opacity=".55"><rect x="252" y="200" width="34" height="9" rx="2"/><rect x="292" y="200" width="34" height="9" rx="2"/><rect x="252" y="212" width="34" height="9" rx="2"/><rect x="292" y="212" width="34" height="9" rx="2"/></g>
<path d="M170 296 A245 245 0 0 1 357 139" fill="none" stroke="url(#rimHi)" stroke-width="5" stroke-linecap="round" filter="url(#b1)"/>
<path d="M630 464 A245 245 0 0 1 484 610" fill="none" stroke="#c9ecff" stroke-opacity=".55" stroke-width="4" stroke-linecap="round" filter="url(#b1)"/>
<circle cx="505" cy="178" r="6" fill="#fff" opacity=".85" filter="url(#b1)"/>
<circle cx="400" cy="380" r="259" fill="none" stroke="#fff" stroke-opacity=".35" stroke-width="1.5"/>
</svg>
</body>
</html>
2. Procedural globe
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Procedural Globe</title>
<style>
:root { --ink:#e8f1ff; --panel:rgba(10,20,40,.55); --line:rgba(140,190,255,.28); --accent:#6fd3ff; }
html, body { margin:0; height:100%; background:#02040a; overflow:hidden;
font-family:"Avenir Next","Segoe UI",system-ui,sans-serif; color:var(--ink); }
canvas { display:block; cursor:grab; touch-action:none; }
canvas.dragging { cursor:grabbing; }
.panel { position:fixed; left:50%; bottom:calc(24px + env(safe-area-inset-bottom, 0px)); transform:translateX(-50%);
width:min(340px, calc(100% - 32px)); box-sizing:border-box; padding:14px 18px;
background:var(--panel); border:1px solid var(--line); border-radius:14px;
backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); }
.row { display:flex; justify-content:space-between; align-items:baseline; margin-bottom:10px; font-size:14px; }
.row output { color:var(--accent); font-variant-numeric:tabular-nums; }
input[type=range] { width:100%; margin:0; accent-color:var(--accent); }
input[type=range]:focus-visible { outline:2px solid var(--accent); outline-offset:4px; }
</style>
</head>
<body>
<div class="panel">
<label class="row" for="speed"><span>Rotation speed</span><output id="speedOut">1.0×</output></label>
<input id="speed" type="range" min="0" max="4" step="0.1" value="1">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(function () {
if (typeof THREE === "undefined") { document.body.insertAdjacentHTML("beforeend", "<p style='padding:24px'>Three.js failed to load.</p>"); return; }
// ---------- Renderer / scene ----------
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.prepend(renderer.domElement);
var canvasEl = renderer.domElement;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
var camDist = 4.6;
camera.position.set(0, 0, camDist);
var sun = new THREE.DirectionalLight(0xfff2dd, 1.25);
sun.position.set(5, 2, 4);
scene.add(sun, new THREE.AmbientLight(0x4a6a9a, 0.55));
var world = new THREE.Group();
world.rotation.x = 0.35;
scene.add(world);
// ---------- Procedural continent texture (seamless 3D value noise) ----------
function hash(x, y, z) {
var h = Math.sin(x * 127.1 + y * 311.7 + z * 74.7) * 43758.5453;
return h - Math.floor(h);
}
function noise(x, y, z) {
var xi = Math.floor(x), yi = Math.floor(y), zi = Math.floor(z);
var xf = x - xi, yf = y - yi, zf = z - zi;
var u = xf * xf * (3 - 2 * xf), v = yf * yf * (3 - 2 * yf), w = zf * zf * (3 - 2 * zf);
function l(a, b, t) { return a + (b - a) * t; }
return l(
l(l(hash(xi, yi, zi), hash(xi + 1, yi, zi), u), l(hash(xi, yi + 1, zi), hash(xi + 1, yi + 1, zi), u), v),
l(l(hash(xi, yi, zi + 1), hash(xi + 1, yi, zi + 1), u), l(hash(xi, yi + 1, zi + 1), hash(xi + 1, yi + 1, zi + 1), u), v),
w);
}
function fbm(x, y, z) {
var s = 0, a = 0.5, f = 1;
for (var i = 0; i < 5; i++) { s += a * noise(x * f, y * f, z * f); f *= 2.05; a *= 0.5; }
return s;
}
function makeEarthTexture() {
var W = 1024, H = 512;
var c = document.createElement("canvas"); c.width = W; c.height = H;
var ctx = c.getContext("2d");
var img = ctx.createImageData(W, H);
var d = img.data, SEA = 0.5;
for (var py = 0; py < H; py++) {
var lat = (0.5 - py / H) * Math.PI;
var cy = Math.sin(lat), r = Math.cos(lat);
for (var px = 0; px < W; px++) {
var lon = (px / W) * Math.PI * 2;
var x = r * Math.cos(lon) * 1.7 + 11, y = cy * 1.7 + 11, z = r * Math.sin(lon) * 1.7 + 11;
var h = fbm(x, y, z);
var R, G, B;
if (h < SEA) {
var t = Math.max(0, Math.min(1, (SEA - h) / 0.25));
R = 22 - 14 * t; G = 88 - 46 * t; B = 150 - 64 * t;
if (SEA - h < 0.025) { R += 30; G += 50; B += 30; }
} else {
var e = (h - SEA) / 0.25;
var dry = Math.max(0, 1 - Math.abs(lat) / 0.9);
var moist = fbm(x * 1.6 + 40, y * 1.6, z * 1.6);
if (e < 0.06) { R = 205; G = 190; B = 132; }
else if (e < 0.55) {
var desert = moist < 0.47 && dry > 0.35 ? 1 : 0;
R = desert ? 196 : 58 + 40 * e; G = desert ? 165 : 128 - 30 * e; B = desert ? 100 : 60;
} else { var m = Math.min(1, e); R = 112 + 90 * m; G = 100 + 90 * m; B = 88 + 90 * m; }
}
var ice = Math.abs(lat) - (1.18 + (fbm(x * 3, y * 3, z * 3) - 0.5) * 0.35);
if (ice > 0) { R = 238; G = 246; B = 252; }
var i = (py * W + px) * 4;
d[i] = R; d[i + 1] = G; d[i + 2] = B; d[i + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
var tex = new THREE.CanvasTexture(c);
tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
return tex;
}
var globe = new THREE.Mesh(
new THREE.SphereGeometry(1.5, 96, 96),
new THREE.MeshPhongMaterial({ map: makeEarthTexture(), shininess: 14, specular: new THREE.Color(0x223344) })
);
world.add(globe);
// ---------- Atmosphere glow ----------
var atmo = new THREE.Mesh(
new THREE.SphereGeometry(1.5 * 1.16, 64, 64),
new THREE.ShaderMaterial({
vertexShader: "varying vec3 vN; void main(){ vN = normalize(normalMatrix * normal); gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }",
fragmentShader: "varying vec3 vN; void main(){ float k = pow(max(0.0, 0.72 - dot(vN, vec3(0.0,0.0,1.0))), 3.0); gl_FragColor = vec4(0.32,0.66,1.0,1.0) * k * 1.6; }",
side: THREE.BackSide, blending: THREE.AdditiveBlending, transparent: true, depthWrite: false
})
);
world.add(atmo);
// ---------- Orbiting ring of light particles ----------
function spriteTexture() {
var c = document.createElement("canvas"); c.width = c.height = 64;
var g = c.getContext("2d"), grad = g.createRadialGradient(32, 32, 0, 32, 32, 32);
grad.addColorStop(0, "rgba(255,255,255,1)"); grad.addColorStop(0.3, "rgba(160,220,255,.7)"); grad.addColorStop(1, "rgba(160,220,255,0)");
g.fillStyle = grad; g.fillRect(0, 0, 64, 64);
return new THREE.CanvasTexture(c);
}
var N = 2200, pos = new Float32Array(N * 3), col = new Float32Array(N * 3);
for (var i = 0; i < N; i++) {
var a = Math.random() * Math.PI * 2, rad = 2.25 + (Math.random() + Math.random() - 1) * 0.28;
pos[i * 3] = Math.cos(a) * rad;
pos[i * 3 + 1] = (Math.random() - 0.5) * 0.06;
pos[i * 3 + 2] = Math.sin(a) * rad;
var mix = Math.random();
col[i * 3] = 0.55 + 0.45 * mix; col[i * 3 + 1] = 0.8 + 0.2 * mix; col[i * 3 + 2] = 1;
}
var ringGeo = new THREE.BufferGeometry();
ringGeo.setAttribute("position", new THREE.BufferAttribute(pos, 3));
ringGeo.setAttribute("color", new THREE.BufferAttribute(col, 3));
var ring = new THREE.Points(ringGeo, new THREE.PointsMaterial({
size: 0.07, map: spriteTexture(), vertexColors: true, transparent: true,
blending: THREE.AdditiveBlending, depthWrite: false, sizeAttenuation: true
}));
ring.rotation.z = 0.32;
world.add(ring);
// ---------- Background stars ----------
var sp = new Float32Array(900 * 3);
for (var s = 0; s < 900; s++) {
var u = Math.random() * 2 - 1, t = Math.random() * Math.PI * 2, q = Math.sqrt(1 - u * u), rr = 30 + Math.random() * 20;
sp[s * 3] = q * Math.cos(t) * rr; sp[s * 3 + 1] = u * rr; sp[s * 3 + 2] = q * Math.sin(t) * rr;
}
var sg = new THREE.BufferGeometry();
sg.setAttribute("position", new THREE.BufferAttribute(sp, 3));
scene.add(new THREE.Points(sg, new THREE.PointsMaterial({ size: 0.12, color: 0xcfe2ff, transparent: true, opacity: 0.8 })));
// ---------- Interaction ----------
var slider = document.getElementById("speed"), out = document.getElementById("speedOut");
var speed = 1;
function syncSpeed() { speed = parseFloat(slider.value); out.textContent = speed.toFixed(1) + "×"; }
slider.addEventListener("input", syncSpeed); syncSpeed();
var pointers = new Map(), dragging = false, velY = 0, pinch = 0;
function setZoom(d) { camDist = Math.min(9, Math.max(2.9, d)); }
canvasEl.addEventListener("pointerdown", function (e) {
canvasEl.setPointerCapture(e.pointerId);
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
dragging = true; canvasEl.classList.add("dragging");
if (pointers.size === 2) { var p = Array.from(pointers.values()); pinch = Math.hypot(p[0].x - p[1].x, p[0].y - p[1].y); }
});
canvasEl.addEventListener("pointermove", function (e) {
var prev = pointers.get(e.pointerId);
if (!prev) return;
var dx = e.clientX - prev.x, dy = e.clientY - prev.y;
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
if (pointers.size === 2) {
var p = Array.from(pointers.values()), dist = Math.hypot(p[0].x - p[1].x, p[0].y - p[1].y);
setZoom(camDist * (pinch / dist)); pinch = dist;
} else {
var k = 0.005 * (camDist / 4.6);
world.rotation.y += dx * k; velY = dx * k;
world.rotation.x = Math.max(-1.3, Math.min(1.3, world.rotation.x + dy * k));
}
});
function endPointer(e) {
pointers.delete(e.pointerId);
if (pointers.size === 0) { dragging = false; canvasEl.classList.remove("dragging"); }
}
canvasEl.addEventListener("pointerup", endPointer);
canvasEl.addEventListener("pointercancel", endPointer);
canvasEl.addEventListener("wheel", function (e) { e.preventDefault(); setZoom(camDist * Math.exp(e.deltaY * 0.001)); }, { passive: false });
window.addEventListener("resize", function () {
camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ---------- Loop ----------
var clock = new THREE.Clock(), zoomNow = camDist;
(function animate() {
requestAnimationFrame(animate);
var dt = Math.min(clock.getDelta(), 0.05);
if (!dragging) { world.rotation.y += velY; velY *= 0.94; world.rotation.y += speed * 0.12 * dt; }
ring.rotation.y += 0.25 * dt;
zoomNow += (camDist - zoomNow) * 0.12;
camera.position.z = zoomNow;
renderer.render(scene, camera);
})();
})();
</script>
</body>
</html>
3. Wizard game
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Aetherspire – Wizard of the Floating Isles</title>
<style>
:root{--panel:rgba(255,250,255,.74);--ink:#3a2f5b;--hp:#ff6f91;--hpbg:rgba(58,47,91,.2);--btn:#ff8fb8;box-sizing:border-box;padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--panel:rgba(30,26,60,.72);--ink:#f3eaff;--hpbg:rgba(255,255,255,.2)}}
:root[data-theme="dark"]{--panel:rgba(30,26,60,.72);--ink:#f3eaff;--hpbg:rgba(255,255,255,.2)}
html,body{height:100%;margin:0;overflow:hidden;background:#1a1d4a;color:var(--ink);font-family:"Trebuchet MS","Segoe UI",system-ui,sans-serif;user-select:none;touch-action:none}
canvas{position:fixed;inset:0;display:block}
#hud{position:fixed;left:0;right:0;top:env(safe-area-inset-top,0px);display:flex;justify-content:space-between;padding:14px;pointer-events:none}
.panel{background:var(--panel);backdrop-filter:blur(6px);border-radius:16px;padding:10px 16px;min-width:150px;font-size:17px;box-shadow:0 4px 18px rgba(60,40,120,.2)}
.panel b{font-size:22px}.right{text-align:right}
.bar{height:12px;border-radius:8px;background:var(--hpbg);margin-top:8px;overflow:hidden}
#hp{display:block;height:100%;width:100%;background:var(--hp);border-radius:8px;transition:width .2s}
#clock{font-size:14px;opacity:.8;margin-top:4px}
#flash{position:fixed;inset:0;background:radial-gradient(transparent 40%,rgba(255,60,90,.55));opacity:0;pointer-events:none}
#tip{position:fixed;bottom:calc(14px + env(safe-area-inset-bottom,0px));left:0;right:0;text-align:center;font-size:13px;color:#fff;text-shadow:0 1px 4px rgba(0,0,0,.5);pointer-events:none}
#overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(40,25,90,.45);backdrop-filter:blur(3px)}
.card{background:var(--panel);border-radius:24px;padding:28px 34px;max-width:min(420px,88vw);text-align:center}
h1{margin:0 0 8px;font-size:32px}p{margin:0 0 18px;line-height:1.5}
button{font:inherit;font-size:18px;font-weight:700;border:0;border-radius:14px;padding:12px 30px;background:var(--btn);color:#3a1240;cursor:pointer}
button:focus-visible{outline:3px solid var(--ink);outline-offset:3px}
small{display:block;margin-top:14px;opacity:.75}
</style>
</head>
<body>
<div id="hud">
<div class="panel"><div>Score <b id="score">0</b></div><div class="bar"><i id="hp"></i></div></div>
<div class="panel right"><div>💎 <b id="cr">0</b> / <span id="tot"></span></div><div id="clock"></div></div>
</div>
<div id="flash"></div>
<div id="tip">WASD move · Space jump · drag to look</div>
<div id="overlay"><div class="card">
<h1 id="ttl">Aetherspire</h1>
<p id="sub">Gather every glowing crystal across the floating isles. Dodge the elemental turrets and don't fall.</p>
<button id="go">Begin</button>
<small>WASD move · Space jump · drag mouse to look</small>
</div></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const $=id=>document.getElementById(id);
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.setSize(innerWidth,innerHeight);
renderer.shadowMap.enabled=true;
document.body.prepend(renderer.domElement);
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x9fdcff);
scene.fog=new THREE.Fog(0x9fdcff,70,300);
const camera=new THREE.PerspectiveCamera(60,innerWidth/innerHeight,.1,800);
const hemi=new THREE.HemisphereLight(0xffffff,0xb9a6ff,.8);scene.add(hemi);
const sun=new THREE.DirectionalLight(0xffffff,1);sun.castShadow=true;
sun.shadow.mapSize.set(2048,2048);
Object.assign(sun.shadow.camera,{left:-55,right:55,top:55,bottom:-55,near:1,far:220});
scene.add(sun,sun.target);
const M=(c,e)=>new THREE.MeshStandardMaterial({color:c,flatShading:true,roughness:.9,emissive:e||0,emissiveIntensity:e?.9:0});
let seed=7;const rnd=()=>{seed=(seed*16807)%2147483647;return (seed-1)/2147483646};
/* ---------- World ---------- */
const islands=[],cols=[],crystals=[],turrets=[],shots=[],bursts=[];
const grass=[0xa8e6b0,0xffd1e8,0xc9b8ff,0xfff1a8,0xa8e0ff];
const EL=[{c:0xff7a5c,rate:2.4,speed:10,range:30,sz:1},{c:0x7fe8ff,rate:3,speed:7.5,range:30,sz:1.3},{c:0xffe66b,rate:3.4,speed:15,range:28,sz:.8}];
const crystalCols=[0x7ff5ff,0xff9ee0,0xb9a0ff];
function addIsland(x,y,z,r,ci){
const g=new THREE.Group();g.position.set(x,y,z);
const top=new THREE.Mesh(new THREE.CylinderGeometry(r,r*.95,1,9),M(grass[ci%5]));
top.position.y=-.5;top.receiveShadow=true;top.castShadow=true;
const h=r*1.1,under=new THREE.Mesh(new THREE.ConeGeometry(r*.95,h,9),M(0xb79fd6));
under.rotation.x=Math.PI;under.position.y=-1-h/2+.05;under.castShadow=true;
g.add(top,under);scene.add(g);
const I={x,y,z,r,occ:[]};islands.push(I);return I;
}
const pt=(I,a,d)=>({x:I.x+Math.cos(a)*d,z:I.z+Math.sin(a)*d});
function addCrystal(I,a,d){
const p=pt(I,a,d),col=crystalCols[crystals.length%3];
const m=new THREE.Mesh(new THREE.OctahedronGeometry(.5),M(col,col));m.scale.y=1.6;
const halo=new THREE.Mesh(new THREE.SphereGeometry(.9,10,8),new THREE.MeshBasicMaterial({color:col,transparent:true,opacity:.22,depthWrite:false}));
const g=new THREE.Group();g.add(m,halo);g.position.set(p.x,I.y+1.3,p.z);scene.add(g);
crystals.push({g,m,y:I.y+1.3,got:false,ph:rnd()*6});I.occ.push(p);
}
function addTurret(I,a,d,el){
const p=pt(I,a,d),e=EL[el],g=new THREE.Group();g.position.set(p.x,I.y,p.z);
const base=new THREE.Mesh(new THREE.CylinderGeometry(.7,.95,.9,8),M(0xd6cbee));base.position.y=.45;
const head=new THREE.Group();head.position.y=1.35;
const core=new THREE.Mesh(new THREE.IcosahedronGeometry(.55,0),M(e.c,e.c));
const barrel=new THREE.Mesh(new THREE.CylinderGeometry(.16,.22,1,6),M(0x8b7fb0));barrel.rotation.x=Math.PI/2;barrel.position.z=.6;
head.add(core,barrel);g.add(base,head);g.traverse(o=>o.castShadow=true);scene.add(g);
cols.push({x:p.x,z:p.z,r:.95,base:I.y,top:I.y+1.9});I.occ.push(p);
turrets.push({head,core,e,cd:1+rnd()*e.rate,x:p.x,y:I.y+1.35,z:p.z});
}
function addTree(I){
for(let t=0;t<40;t++){
const a=rnd()*6.28,d=I.r*(.3+rnd()*.55),p=pt(I,a,d);
if(I.occ.some(o=>Math.hypot(o.x-p.x,o.z-p.z)<3.2)||Math.hypot(p.x,p.z)<3)continue;
const g=new THREE.Group();g.position.set(p.x,I.y,p.z);
const tr=new THREE.Mesh(new THREE.CylinderGeometry(.2,.3,1.3,6),M(0xd9b08c));tr.position.y=.65;
const c1=new THREE.Mesh(new THREE.ConeGeometry(1.1,1.6,7),M(rnd()<.5?0x8fd9a8:0xffb3d1));c1.position.y=1.9;
const c2=new THREE.Mesh(new THREE.ConeGeometry(.8,1.3,7),M(rnd()<.5?0x9be3b4:0xffc6de));c2.position.y=2.8;
g.add(tr,c1,c2);g.traverse(o=>o.castShadow=true);scene.add(g);
cols.push({x:p.x,z:p.z,r:.55,base:I.y,top:I.y+3.4});I.occ.push(p);return;
}
}
const hub=addIsland(0,0,0,9,0);
const ringY=[1,.5,1.5,1,.5];
for(let i=0;i<5;i++){
const a=i*Math.PI*2/5+.3,ring=addIsland(Math.cos(a)*19.5,ringY[i],Math.sin(a)*19.5,6.5,i+1);
const out=addIsland(Math.cos(a)*36,ringY[i]+1.5,Math.sin(a)*36,6,i+2);
[[ring,1],[out,2]].forEach(([I,n])=>{
const ta=a+(rnd()-.5)*1.2;
if(I===out||i%2===0)addTurret(I,ta,I.r*.6,(i+(I===out?1:0))%3);
for(let k=0;k<n;k++)addCrystal(I,ta+Math.PI+(k-.5)*1.3,I.r*.5);
});
}
islands.forEach(I=>{for(let k=0;k<4;k++)addTree(I)});
$('tot').textContent=crystals.length;
/* ---------- Sky, sun, stars, clouds, pollen ---------- */
const sunM=new THREE.Mesh(new THREE.SphereGeometry(14,16,12),new THREE.MeshBasicMaterial({color:0xfff2b0,fog:false}));
const moonM=new THREE.Mesh(new THREE.SphereGeometry(9,16,12),new THREE.MeshBasicMaterial({color:0xe8ecff,fog:false}));
scene.add(sunM,moonM);
const sp=new Float32Array(1200);
for(let i=0;i<400;i++){const u=rnd()*6.28,v=rnd()*1.4+.05,r=450;sp.set([Math.cos(u)*Math.cos(v)*r,Math.sin(v)*r,Math.sin(u)*Math.cos(v)*r],i*3)}
const sg=new THREE.BufferGeometry();sg.setAttribute('position',new THREE.BufferAttribute(sp,3));
const stars=new THREE.Points(sg,new THREE.PointsMaterial({color:0xffffff,size:2.2,sizeAttenuation:false,transparent:true,opacity:0,fog:false,depthWrite:false}));
scene.add(stars);
const cloudMat=new THREE.MeshStandardMaterial({color:0xffffff,flatShading:true,roughness:1,transparent:true,opacity:.92});
const clouds=[];
for(let i=0;i<16;i++){
const g=new THREE.Group();
for(let k=0;k<4;k++){const b=new THREE.Mesh(new THREE.IcosahedronGeometry(3+rnd()*3,0),cloudMat);b.position.set(k*4-6,rnd()*1.5,rnd()*3);b.scale.y=.6;g.add(b)}
g.position.set((rnd()-.5)*300,-8+rnd()*40,(rnd()-.5)*300);scene.add(g);clouds.push(g);
}
const NP=300,pp=new Float32Array(NP*3);
for(let i=0;i<NP;i++)pp.set([(rnd()-.5)*140,-5+rnd()*35,(rnd()-.5)*140],i*3);
const pg=new THREE.BufferGeometry();pg.setAttribute('position',new THREE.BufferAttribute(pp,3));
const pmat=new THREE.PointsMaterial({color:0xfff0b0,size:.45,transparent:true,opacity:.85,depthWrite:false,blending:THREE.AdditiveBlending});
const pollen=new THREE.Points(pg,pmat);scene.add(pollen);
const SKY=[0xffc9dc,0x9fdcff,0xffb59a,0x181c48].map(c=>new THREE.Color(c));
let dayT=.18;
function updateSky(dt,pl){
dayT=(dayT+dt/100)%1;
const f=dayT*4,i=Math.floor(f),k=f-i,s=k*k*(3-2*k);
const c=SKY[i].clone().lerp(SKY[(i+1)%4],s);
scene.background.copy(c);scene.fog.color.copy(c);
const a=dayT*Math.PI*2,sa=Math.sin(a),dir=new THREE.Vector3(Math.cos(a),sa,.35).normalize();
const day=Math.max(0,sa),night=Math.max(0,Math.min(1,-sa*2.2+.2));
sun.position.copy(pl).addScaledVector(sa>=0?dir:dir.clone().negate(),90);sun.target.position.copy(pl);
sun.intensity=sa>=0?.3+.9*day:.25;
sun.color.setHex(sa>=0?0xfff1e0:0xa9b8ff).lerp(new THREE.Color(0xffb38a),sa>=0?Math.max(0,1-day*3):0);
hemi.intensity=.3+.55*day;
sunM.position.copy(pl).addScaledVector(dir,380);moonM.position.copy(pl).addScaledVector(dir,-380);
stars.position.copy(camera.position);stars.material.opacity=night;
cloudMat.color.setScalar(.4+.6*Math.min(1,day*1.6+.2)).lerp(c,.15);
pmat.color.setHex(night>.5?0xb8ffb0:0xfff0b0);
pl.night=night;
$('clock').textContent=sa>.35?'\u2600\uFE0F Day':sa>-.1?(dayT<.25?'\uD83C\uDF05 Dawn':'\uD83C\uDF07 Dusk'):'\uD83C\uDF19 Night';
}
/* ---------- Player ---------- */
const wiz=new THREE.Group();
(function(){
const add=(geo,mat,x,y,z,rz,rx)=>{const m=new THREE.Mesh(geo,mat);m.position.set(x,y,z);if(rz)m.rotation.z=rz;if(rx)m.rotation.x=rx;wiz.add(m);return m};
add(new THREE.ConeGeometry(.6,1.5,7),M(0x7f6bff),0,.75,0);
add(new THREE.IcosahedronGeometry(.3,0),M(0xffdcc2),0,1.65,0);
add(new THREE.ConeGeometry(.2,.55,5),M(0xffffff),0,1.4,.22,0,Math.PI*.85);
add(new THREE.CylinderGeometry(.6,.6,.06,8),M(0x5a48d8),0,1.88,0);
add(new THREE.ConeGeometry(.38,1,7),M(0x5a48d8),0,2.35,0,.15);
add(new THREE.SphereGeometry(.08,6,5),M(0xfff07a,0xfff07a),.12,2.2,.3);
add(new THREE.CylinderGeometry(.05,.05,1.9,5),M(0xd9b38c),.62,.95,.2);
add(new THREE.IcosahedronGeometry(.17,1),new THREE.MeshBasicMaterial({color:0x9ff3ff}),.62,1.95,.2);
const l=new THREE.PointLight(0xbfe6ff,0,14);l.position.set(.62,1.95,.2);wiz.add(l);wiz.userData.light=l;
wiz.traverse(o=>o.castShadow=true);
})();
scene.add(wiz);
const P={pos:new THREE.Vector3(),vel:new THREE.Vector3(),ground:false,coy:0,buf:0,face:0,inv:0,hp:100,score:0,got:0,safe:new THREE.Vector3(),night:0};
let state='menu',yaw=0,pitch=.38,best=0;
try{best=+localStorage.getItem('aetherspire-best')||0}catch(e){}
/* ---------- Audio ---------- */
let AC=null;
function initAudio(){try{if(!AC)AC=new(window.AudioContext||window.webkitAudioContext)();if(AC.state==='suspended')AC.resume()}catch(e){}}
function tone(f,d,type,v,f2,delay){
if(!AC)return;const t=AC.currentTime+(delay||0),o=AC.createOscillator(),g=AC.createGain();
o.type=type||'sine';o.frequency.setValueAtTime(f,t);o.frequency.exponentialRampToValueAtTime(f2||f,t+d);
g.gain.setValueAtTime(v||.15,t);g.gain.exponentialRampToValueAtTime(.001,t+d);
o.connect(g);g.connect(AC.destination);o.start(t);o.stop(t+d+.02);
}
const sfx={
jump:()=>tone(320,.18,'sine',.18,760),
collect:()=>[660,880,1320].forEach((f,i)=>tone(f,.25,'triangle',.16,f*1.01,i*.07)),
hurt:()=>tone(240,.32,'sawtooth',.18,70),
shoot:()=>tone(520,.12,'square',.035,180),
over:()=>[400,300,220,150].forEach((f,i)=>tone(f,.35,'triangle',.18,f*.8,i*.18)),
win:()=>[523,659,784,1047,1319].forEach((f,i)=>tone(f,.4,'triangle',.16,f,i*.1))
};
/* ---------- Game flow ---------- */
function hurt(n){
if(P.inv>0||state!=='play')return;
P.hp=Math.max(0,P.hp-n);P.inv=1;sfx.hurt();flashA=1;
if(P.hp<=0)end(false);
}
let flashA=0;
function burst(p,col){
for(let i=0;i<14;i++){
const m=new THREE.Mesh(new THREE.TetrahedronGeometry(.16),new THREE.MeshBasicMaterial({color:col}));
m.position.copy(p);scene.add(m);
bursts.push({m,v:new THREE.Vector3((rnd()-.5)*7,rnd()*6+1,(rnd()-.5)*7),t:.8});
}
}
function respawn(){P.pos.copy(P.safe);P.vel.set(0,0,0)}
function reset(){
P.hp=100;P.score=0;P.got=0;P.inv=0;P.safe.set(0,0,0);respawn();
crystals.forEach(c=>{c.got=false;c.g.visible=true});
shots.forEach(s=>scene.remove(s.m));shots.length=0;
turrets.forEach(t=>t.cd=1.5+rnd()*t.e.rate);
hud();
}
function end(win){
state='over';
if(win)P.score+=P.hp*5;
if(P.score>best){best=P.score;try{localStorage.setItem('aetherspire-best',best)}catch(e){}}
(win?sfx.win:sfx.over)();
$('ttl').textContent=win?'Archipelago restored!':'Game over';
$('sub').textContent=`Score ${P.score} \u00b7 Crystals ${P.got}/${crystals.length} \u00b7 Best ${best}`;
$('go').textContent=win?'Play again':'Try again';
$('overlay').style.display='flex';hud();
}
function start(){initAudio();reset();state='play';$('overlay').style.display='none'}
$('go').onclick=start;
function hud(){
$('score').textContent=P.score;$('cr').textContent=P.got;
$('hp').style.width=P.hp+'%';
}
/* ---------- Input ---------- */
const keys={};let drag=false;
addEventListener('keydown',e=>{
if(['Space','ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.code))e.preventDefault();
if(e.repeat)return;keys[e.code]=true;
if(e.code==='Space')P.buf=.14;
if((e.code==='Enter'||e.code==='KeyR')&&state!=='play')start();
});
addEventListener('keyup',e=>keys[e.code]=false);
addEventListener('blur',()=>{for(const k in keys)keys[k]=false});
const cv=renderer.domElement;
cv.addEventListener('pointerdown',e=>{drag=true;cv.setPointerCapture(e.pointerId)});
cv.addEventListener('pointerup',()=>drag=false);
cv.addEventListener('pointermove',e=>{
if(!drag)return;
yaw-=e.movementX*.006;pitch=Math.max(-.15,Math.min(1.2,pitch+e.movementY*.005));
});
addEventListener('resize',()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight)});
/* ---------- Update ---------- */
const tmp=new THREE.Vector3();
function updatePlayer(dt){
const ix=(keys.KeyD||keys.ArrowRight?1:0)-(keys.KeyA||keys.ArrowLeft?1:0);
const iz=(keys.KeyW||keys.ArrowUp?1:0)-(keys.KeyS||keys.ArrowDown?1:0);
const fx=-Math.sin(yaw),fz=-Math.cos(yaw);
let dx=fx*iz-fz*ix,dz=fz*iz+fx*ix;const l=Math.hypot(dx,dz);
const moving=l>0;if(moving){dx/=l;dz/=l}
const rate=P.ground?14:5,k=1-Math.exp(-rate*dt);
P.vel.x+=(dx*8-P.vel.x)*k;P.vel.z+=(dz*8-P.vel.z)*k;
if(moving){
let d=Math.atan2(dx,dz)-P.face;d=Math.atan2(Math.sin(d),Math.cos(d));P.face+=d*Math.min(1,dt*14);
}
P.vel.y-=24*dt;
P.buf-=dt;P.coy-=dt;
if(P.buf>0&&P.coy>0){P.vel.y=10.5;P.buf=0;P.coy=0;P.ground=false;sfx.jump()}
const prevY=P.pos.y;
P.pos.addScaledVector(P.vel,dt);
// obstacle + island wall collisions
for(const c of cols){
if(P.pos.y>=c.top||P.pos.y+1.6<=c.base)continue;
const ox=P.pos.x-c.x,oz=P.pos.z-c.z,d=Math.hypot(ox,oz),m=c.r+.4;
if(d<m){const n=d||1;P.pos.x=c.x+ox/n*m;P.pos.z=c.z+oz/n*m}
}
for(const I of islands){
const d=Math.hypot(P.pos.x-I.x,P.pos.z-I.z);
if(P.pos.y<I.y-.05&&P.pos.y>I.y-1.2&&d<I.r+.4){
const n=d||1;P.pos.x=I.x+(P.pos.x-I.x)/n*(I.r+.4);P.pos.z=I.z+(P.pos.z-I.z)/n*(I.r+.4);
}
}
P.ground=false;
for(const I of islands){
const d=Math.hypot(P.pos.x-I.x,P.pos.z-I.z);
if(d<I.r-.05&&P.vel.y<=0&&prevY>=I.y-.05&&P.pos.y<=I.y){
P.pos.y=I.y;P.vel.y=0;P.ground=true;
if(d<I.r*.7)P.safe.set(P.pos.x,I.y,P.pos.z);
}
}
if(P.ground)P.coy=.1;
if(P.pos.y<-28){hurt(20);if(state==='play')respawn()}
P.inv=Math.max(0,P.inv-dt);
wiz.position.copy(P.pos);
wiz.position.y+=P.ground&&moving?Math.abs(Math.sin(performance.now()*.012))*.12:0;
wiz.rotation.y=P.face;
wiz.visible=P.inv<=0||Math.floor(P.inv*14)%2===0;
wiz.userData.light.intensity=P.night*1.4;
}
function updateWorld(dt,t){
crystals.forEach(c=>{
if(c.got)return;
c.m.rotation.y+=dt*1.6;
c.g.position.y=c.y+Math.sin(t*2+c.ph)*.25;
if(state==='play'&&Math.hypot(c.g.position.x-P.pos.x,c.g.position.y-P.pos.y-1,c.g.position.z-P.pos.z)<1.7){
c.got=true;c.g.visible=false;P.got++;P.score+=100;sfx.collect();burst(c.g.position,c.m.material.color.getHex());hud();
if(P.got===crystals.length)end(true);
}
});
const target=tmp.set(P.pos.x,P.pos.y+1,P.pos.z);
turrets.forEach(u=>{
const dx=target.x-u.x,dy=target.y-u.y,dz=target.z-u.z,dist=Math.hypot(dx,dy,dz);
if(state==='play'&&dist<u.e.range){
let d=Math.atan2(dx,dz)-u.head.rotation.y;d=Math.atan2(Math.sin(d),Math.cos(d));u.head.rotation.y+=d*Math.min(1,dt*6);
u.cd-=dt;
u.core.scale.setScalar(1+(u.cd<.5?(1-u.cd/.5)*.6:0));
if(u.cd<=0){
u.cd=u.e.rate;u.core.scale.setScalar(1);
const v=new THREE.Vector3(dx,dy,dz).normalize(),m=new THREE.Mesh(new THREE.SphereGeometry(.28*u.e.sz,8,6),new THREE.MeshBasicMaterial({color:u.e.c}));
m.position.set(u.x,u.y,u.z).addScaledVector(v,1.1);scene.add(m);
shots.push({m,v:v.multiplyScalar(u.e.speed),life:5});
if(dist<28)sfx.shoot();
}
}else{u.head.rotation.y+=dt*.5;u.core.scale.setScalar(1)}
});
for(let i=shots.length-1;i>=0;i--){
const s=shots[i];s.m.position.addScaledVector(s.v,dt);s.life-=dt;
let dead=s.life<=0;
if(state==='play'&&s.m.position.distanceTo(target)<.95){hurt(10);dead=true}
if(dead){scene.remove(s.m);shots.splice(i,1)}
}
for(let i=bursts.length-1;i>=0;i--){
const b=bursts[i];b.t-=dt;b.v.y-=12*dt;b.m.position.addScaledVector(b.v,dt);b.m.scale.setScalar(Math.max(.01,b.t/.8));
if(b.t<=0){scene.remove(b.m);bursts.splice(i,1)}
}
clouds.forEach(c=>{c.position.x+=dt*1.2;if(c.position.x>170)c.position.x=-170});
const a=pg.attributes.position.array;
for(let i=0;i<NP;i++){
a[i*3]+=Math.sin(t*.5+i)*.4*dt;a[i*3+1]+=Math.sin(t*.8+i*1.7)*.35*dt;a[i*3+2]+=Math.cos(t*.4+i)*.4*dt;
if(a[i*3]-P.pos.x>70)a[i*3]-=140;else if(a[i*3]-P.pos.x<-70)a[i*3]+=140;
if(a[i*3+2]-P.pos.z>70)a[i*3+2]-=140;else if(a[i*3+2]-P.pos.z<-70)a[i*3+2]+=140;
}
pg.attributes.position.needsUpdate=true;
}
function updateCamera(dt){
const cp=Math.cos(pitch),dist=9;
const want=new THREE.Vector3(Math.sin(yaw)*cp,Math.sin(pitch),Math.cos(yaw)*cp).multiplyScalar(dist).add(P.pos).add(new THREE.Vector3(0,1.7,0));
camera.position.lerp(want,1-Math.exp(-10*dt));
camera.lookAt(P.pos.x,P.pos.y+1.6,P.pos.z);
}
/* ---------- Loop ---------- */
reset();state='menu';camera.position.set(0,6,10);
const clock=new THREE.Clock();
(function loop(){
requestAnimationFrame(loop);
const dt=Math.min(clock.getDelta(),.05),t=clock.elapsedTime;
if(state==='play')updatePlayer(dt);
else{yaw+=dt*.15;wiz.position.copy(P.pos);wiz.rotation.y=P.face}
updateWorld(dt,t);updateSky(dt,P.pos);updateCamera(dt);
if(state==='play')hud();
flashA=Math.max(0,flashA-dt*2.5);$('flash').style.opacity=flashA;
renderer.render(scene,camera);
})();
</script>
</body>
</html>
What this says about Sonnet 5.5
Read Next
GPT-6 Luna Browser Coding Test: Aetherfall, Pelagic & Glass Crane
A hands-on look at three GPT-6 Luna creative-coding artifacts: the Aetherfall wizard game, Pelagic procedural globe, and glass origami crane, with interactive previews and scope notes.
GPT-6 Sol Browser Coding Test: Floating Isles, Blue Horizon & Glass Crane
Explore three supplied GPT-6 Sol browser artifacts: Floating Isles wizard quest, Blue Horizon procedural globe, and a glass-sphere origami crane, embedded as isolated previews.
Grok 4.7 Browser Coding Test: Glass Crane, Procedural Globe, and a Game Cut Short
Hands-on Grok 4.7 CLI test with embedded SVG crane and interactive Three.js globe HTML, browser observations, and an explicit account of the wizard-game request that failed at the CLI quota limit.