← Back to Articles Directory
AI Models September 21, 2026 3 min read

Grok 4.7 Browser Coding Test: Glass Crane, Procedural Globe, and a Game Cut Short

Two Grok CLI HTML outputs were rendered and tested in Chromium. A third wizard-game request hit the account's rolling usage limit before it generated a file.

Mohid Mirza

Co-Founder & Lead Programmer of AcceleratedLogic AI

I sent three browser-building prompts to the Grok CLI with --model grok-4.7 on September 21, 2026: an SVG glass sphere containing an origami crane, an interactive procedural Three.js globe, and a third-person wizard game. The CLI listed grok-4.7 as an available model. Two requests returned complete HTML. The game request was rejected after the account reached its rolling free-use limit, so there is no Grok 4.7 game result to judge or embed.
This is a small hands-on coding test, not a benchmark of the model's general intelligence. I kept the downloadable and embedded outputs as delivered, opened the HTML files in Chromium, tested the interactive controls where present, checked browser errors, and inspected screenshots. The site's hosted demo copies add only a crawler noindex tag. I used isolated temporary directories so the CLI could not copy earlier examples from this repository. The SVG run used the CLI's default reasoning setting; the globe run used low reasoning. Both runs used a short system instruction asking for a single HTML document and no tool calls. That difference in settings matters when comparing their speed or cost, so I do not make either comparison here.

Test 1: SVG crane inside glass

The first prompt asked for a photorealistic 3D glass sphere containing a detailed 3D origami crane, rendered as a fully working single HTML file. Grok returned a 21 KB document with one SVG, 47 path elements, layered gradients, blur and shadow filters, translucent rims, reflected arcs, and colored edge offsets that suggest refraction. It also includes a title and description for the illustration and a reduced-motion rule for its gentle floating animation.
The visual result is coherent: the bird is clearly folded paper, the sphere has convincing highlights and depth, and the contact shadow grounds the composition. The optics are artistic approximations, and the crane is a faceted 2D SVG construction rather than a true 3D mesh. That is a reasonable single-file vector solution, but it falls short of literal photorealism. Chromium rendered it without page or console errors in my smoke test.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Glass Sphere — Origami Crane</title>
<style>
  html, body {
    margin: 0;
    height: 100%;
    background: #0b0e13;
  }
  body {
    display: grid;
    place-items: center;
    min-height: 100vh;
    overflow: hidden;
  }
  svg {
    width: min(100vw, calc(100vh * 0.9));
    height: auto;
    max-height: 100vh;
    display: block;
    user-select: none;
  }
  .float { animation: float 8s ease-in-out infinite; }
  .shadow {
    transform-box: fill-box;
    transform-origin: center;
    animation: shadow 8s ease-in-out infinite;
  }
  .spec { animation: spec 9.5s ease-in-out infinite; }
  @keyframes float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-13px); }
  }
  @keyframes shadow {
    0%, 100% { transform: scaleX(1); opacity: 1; }
    50% { transform: scaleX(0.9); opacity: 0.72; }
  }
  @keyframes spec {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(9px, 7px); }
  }
  @media (prefers-reduced-motion: reduce) {
    .float, .shadow, .spec { animation: none; }
  }
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 900 1000" role="img" aria-labelledby="title desc" color-interpolation-filters="sRGB">
  <title id="title">Photorealistic glass sphere containing a paper origami crane</title>
  <desc id="desc">A clear studio glass orb with specular highlights, rim refraction, and a fully creased origami crane suspended inside.</desc>
  <defs>
    <radialGradient id="bg" cx="50%" cy="38%" r="75%">
      <stop offset="0%" stop-color="#243041"/>
      <stop offset="48%" stop-color="#141b26"/>
      <stop offset="100%" stop-color="#080b10"/>
    </radialGradient>
    <radialGradient id="spot" cx="300" cy="160" r="560" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#f4f1ea" stop-opacity="0.18"/>
      <stop offset="42%" stop-color="#c5d2e0" stop-opacity="0.05"/>
      <stop offset="100%" stop-color="#0b0e13" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="vignette" cx="50%" cy="40%" r="72%">
      <stop offset="55%" stop-color="#000" stop-opacity="0"/>
      <stop offset="100%" stop-color="#000" stop-opacity="0.42"/>
    </radialGradient>
    <linearGradient id="floor" x1="0" y1="720" x2="0" y2="1000" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#1a222c" stop-opacity="0"/>
      <stop offset="16%" stop-color="#18212b"/>
      <stop offset="100%" stop-color="#0a0d12"/>
    </linearGradient>
    <radialGradient id="floorReflect" cx="50%" cy="50%" r="50%">
      <stop offset="0%" stop-color="#d5e6f4" stop-opacity="0.28"/>
      <stop offset="55%" stop-color="#7f98ad" stop-opacity="0.08"/>
      <stop offset="100%" stop-color="#7f98ad" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="interior" cx="390" cy="360" r="360" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#7d90a6"/>
      <stop offset="32%" stop-color="#46566a"/>
      <stop offset="68%" stop-color="#283444"/>
      <stop offset="100%" stop-color="#121920"/>
    </radialGradient>
    <radialGradient id="transmit" cx="355" cy="320" r="240" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#ffffff" stop-opacity="0.28"/>
      <stop offset="45%" stop-color="#d7e8f8" stop-opacity="0.08"/>
      <stop offset="100%" stop-color="#d7e8f8" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="veil" cx="420" cy="400" r="300" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#ffffff" stop-opacity="0.02"/>
      <stop offset="55%" stop-color="#c5daf0" stop-opacity="0.05"/>
      <stop offset="100%" stop-color="#f7fbff" stop-opacity="0.16"/>
    </radialGradient>
    <radialGradient id="fresnel" cx="450" cy="450" r="300" gradientUnits="userSpaceOnUse">
      <stop offset="68%" stop-color="#ffffff" stop-opacity="0"/>
      <stop offset="86%" stop-color="#e7f3ff" stop-opacity="0.14"/>
      <stop offset="96%" stop-color="#ffffff" stop-opacity="0.45"/>
      <stop offset="100%" stop-color="#ffffff" stop-opacity="0.9"/>
    </radialGradient>
    <linearGradient id="rimStroke" x1="200" y1="200" x2="720" y2="720" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#ffffff"/>
      <stop offset="35%" stop-color="#d5e8f6"/>
      <stop offset="62%" stop-color="#8eafc6"/>
      <stop offset="100%" stop-color="#f5fbff"/>
    </linearGradient>
    <radialGradient id="specGrad" cx="35%" cy="35%" r="65%">
      <stop offset="0%" stop-color="#ffffff" stop-opacity="0.95"/>
      <stop offset="40%" stop-color="#ffffff" stop-opacity="0.35"/>
      <stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>
    </radialGradient>
    <radialGradient id="edgeFade" cx="450" cy="450" r="300" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#000"/>
      <stop offset="58%" stop-color="#000"/>
      <stop offset="78%" stop-color="#fff" stop-opacity="0.35"/>
      <stop offset="100%" stop-color="#fff"/>
    </radialGradient>
    <mask id="rimMask">
      <circle cx="450" cy="450" r="300" fill="url(#edgeFade)"/>
    </mask>
    <clipPath id="orb">
      <circle cx="450" cy="450" r="300"/>
    </clipPath>
    <linearGradient id="gLeftUp" x1="230" y1="330" x2="450" y2="500" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#fffefb"/>
      <stop offset="45%" stop-color="#f7efe3"/>
      <stop offset="100%" stop-color="#e7d5c0"/>
    </linearGradient>
    <linearGradient id="gLeftLo" x1="210" y1="420" x2="460" y2="550" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#efe4d6"/>
      <stop offset="55%" stop-color="#d5cdc2"/>
      <stop offset="100%" stop-color="#b7c4cb"/>
    </linearGradient>
    <linearGradient id="gRightUp" x1="700" y1="340" x2="470" y2="500" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#fbfaf7"/>
      <stop offset="50%" stop-color="#e4e8ea"/>
      <stop offset="100%" stop-color="#c9d3d6"/>
    </linearGradient>
    <linearGradient id="gRightLo" x1="710" y1="420" x2="470" y2="540" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#dde3e4"/>
      <stop offset="100%" stop-color="#a9b6bc"/>
    </linearGradient>
    <linearGradient id="gBodyUp" x1="420" y1="440" x2="520" y2="510" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#fffefb"/>
      <stop offset="100%" stop-color="#f3e7d8"/>
    </linearGradient>
    <linearGradient id="gBodyLo" x1="390" y1="460" x2="460" y2="545" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#eadfce"/>
      <stop offset="100%" stop-color="#b9c4c8"/>
    </linearGradient>
    <linearGradient id="gNeck" x1="450" y1="470" x2="600" y2="260" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#f6ecdf"/>
      <stop offset="55%" stop-color="#fffaf4"/>
      <stop offset="100%" stop-color="#f3e6d6"/>
    </linearGradient>
    <linearGradient id="gHead" x1="560" y1="250" x2="640" y2="300" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#fffefb"/>
      <stop offset="100%" stop-color="#f4e7d6"/>
    </linearGradient>
    <linearGradient id="gHeadLo" x1="560" y1="270" x2="640" y2="310" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#f0e4d4"/>
      <stop offset="100%" stop-color="#d4cbc0"/>
    </linearGradient>
    <linearGradient id="gTail" x1="460" y1="500" x2="290" y2="660" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#f4ebdf"/>
      <stop offset="100%" stop-color="#c5d0d4"/>
    </linearGradient>
    <linearGradient id="gTailLo" x1="460" y1="520" x2="300" y2="670" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#d9e0e2"/>
      <stop offset="100%" stop-color="#aebbc2"/>
    </linearGradient>
    <linearGradient id="gGusset" x1="350" y1="420" x2="520" y2="510" gradientUnits="userSpaceOnUse">
      <stop offset="0%" stop-color="#f7f0e6"/>
      <stop offset="100%" stop-color="#d9cfc0"/>
    </linearGradient>
    <filter id="contact" x="-80%" y="-220%" width="260%" height="540%">
      <feGaussianBlur stdDeviation="10"/>
    </filter>
    <filter id="contactWide" x="-80%" y="-220%" width="260%" height="540%">
      <feGaussianBlur stdDeviation="22"/>
    </filter>
    <filter id="haloBlur" x="-60%" y="-60%" width="220%" height="220%">
      <feGaussianBlur stdDeviation="26"/>
    </filter>
    <filter id="soft" x="-120%" y="-120%" width="340%" height="340%">
      <feGaussianBlur stdDeviation="8"/>
    </filter>
    <filter id="softBig" x="-120%" y="-120%" width="340%" height="340%">
      <feGaussianBlur stdDeviation="16"/>
    </filter>
    <filter id="craneShadow" x="-50%" y="-50%" width="200%" height="200%">
      <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="b"/>
      <feOffset in="b" dx="8" dy="18" result="o"/>
      <feFlood flood-color="#070b10" flood-opacity="0.5" result="f"/>
      <feComposite in="f" in2="o" operator="in"/>
    </filter>
    <filter id="paperGrain" x="-8%" y="-8%" width="116%" height="116%">
      <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="4" result="n"/>
      <feComponentTransfer in="n" result="g">
        <feFuncR type="linear" slope="0.35" intercept="0.55"/>
        <feFuncG type="linear" slope="0.3" intercept="0.52"/>
        <feFuncB type="linear" slope="0.25" intercept="0.48"/>
        <feFuncA type="linear" slope="0.09"/>
      </feComponentTransfer>
      <feBlend in="SourceGraphic" in2="g" mode="multiply"/>
    </filter>
    <filter id="disperse" x="-20%" y="-20%" width="140%" height="140%">
      <feColorMatrix type="matrix" values="0.15 0 0 0 0.12  0 0.45 0 0 0.38  0 0 0.85 0 0.62  0 0 0 0.7 0" result="c"/>
      <feGaussianBlur in="c" stdDeviation="0.7"/>
    </filter>
    <filter id="redFringe" x="-20%" y="-20%" width="140%" height="140%">
      <feColorMatrix type="matrix" values="1 0 0 0 0.25  0 0.18 0 0 0.04  0 0 0.15 0 0.04  0 0 0 0.6 0" result="c"/>
      <feGaussianBlur in="c" stdDeviation="0.6"/>
    </filter>
    <g id="crane" stroke-linejoin="round" stroke-linecap="round">
      <ellipse cx="455" cy="518" rx="78" ry="16" fill="#1a140f" opacity="0.22" filter="url(#soft)"/>
      <path d="M450,518 C396,558 352,600 318,640 L328,650 C364,608 406,566 462,526 Z" fill="url(#gTailLo)" stroke="#93a4ab" stroke-width="0.8"/>
      <path d="M444,510 C388,552 342,596 300,642 L314,652 C356,604 400,560 458,518 Z" fill="url(#gTail)" stroke="#d5c8b6" stroke-width="0.8"/>
      <polygon points="300,642 314,652 296,658" fill="#8ea0a8" stroke="#7d9098" stroke-width="0.6"/>
      <polygon points="704,418 712,428 654,530 648,522" fill="#7f8c93"/>
      <polygon points="478,466 704,418 648,522 470,502" fill="url(#gRightLo)" stroke="#8ea0a8" stroke-width="0.9"/>
      <polygon points="478,466 655,348 704,418" fill="url(#gRightUp)" stroke="#e7eeea" stroke-width="0.9"/>
      <polygon points="704,418 680,406 686,434" fill="#d5dddd" stroke="#c5d0d0" stroke-width="0.6"/>
      <polygon points="206,422 198,432 266,538 272,530" fill="#8a8178"/>
      <polygon points="438,468 206,422 272,530 452,506" fill="url(#gLeftLo)" stroke="#9aa8ae" stroke-width="0.9"/>
      <polygon points="438,468 258,340 206,422" fill="url(#gLeftUp)" stroke="#f3eadc" stroke-width="1"/>
      <polygon points="206,422 232,408 226,436" fill="#e6d9c6" stroke="#d9cbb6" stroke-width="0.6"/>
      <polygon points="424,464 350,420 405,490" fill="url(#gGusset)" stroke="#ddcfc0" stroke-width="0.6"/>
      <polygon points="498,456 570,418 515,492" fill="#e7ddd2" stroke="#d5c9ba" stroke-width="0.6"/>
      <polygon points="410,476 334,438 392,508" fill="#f4ebdf" stroke="#e4d8c8" stroke-width="0.5"/>
      <polygon points="508,470 586,438 528,506" fill="#e1e6e6" stroke="#c9d3d4" stroke-width="0.5"/>
      <polygon points="434,466 499,458 516,502 456,510" fill="#ddd2c4"/>
      <polygon points="434,470 456,512 446,536 396,494" fill="#c5cdd1"/>
      <polygon points="430,460 495,452 512,496 452,504" fill="url(#gBodyUp)" stroke="#f7f1e6" stroke-width="0.9"/>
      <polygon points="430,460 452,504 444,532 392,488" fill="url(#gBodyLo)" stroke="#b7c3c8" stroke-width="0.8"/>
      <polygon points="448,508 468,512 452,528 432,516" fill="#6d645c" opacity="0.35"/>
      <path d="M456,462 C498,400 536,328 578,276 L590,290 C546,342 508,414 472,474 Z" fill="url(#gNeck)" stroke="#eadccb" stroke-width="0.8"/>
      <polygon points="566,274 606,286 574,294" fill="#e7dac8"/>
      <polygon points="566,276 604,254 632,270 606,288 572,294" fill="url(#gHeadLo)" stroke="#ddcfc0" stroke-width="0.7"/>
      <polygon points="566,276 604,254 632,270 600,272" fill="url(#gHead)" stroke="#fffaf3" stroke-width="0.8"/>
      <g fill="none" stroke-linecap="round" stroke-linejoin="round">
        <path d="M438,468 L206,422" stroke="#fffaf4" stroke-width="1.35" opacity="0.85"/>
        <path d="M445,492 L248,500" stroke="#8d7d6c" stroke-width="0.9" opacity="0.7"/>
        <path d="M420,470 L310,368" stroke="#c3b3a2" stroke-width="0.7" opacity="0.75"/>
        <path d="M428,486 L278,432" stroke="#b5a594" stroke-width="0.7" opacity="0.7"/>
        <path d="M432,502 L308,492" stroke="#9aabB2" stroke-width="0.7" opacity="0.65"/>
        <path d="M400,470 L290,384 L248,428 L302,502 L418,500" stroke="#b7a894" stroke-width="0.85" opacity="0.8"/>
        <path d="M370,450 L382,436" stroke="#fff" stroke-width="0.8" opacity="0.55"/>
        <path d="M310,440 L322,428" stroke="#8a7b6c" stroke-width="0.8" opacity="0.6"/>
        <path d="M252,430 L262,420" stroke="#fff" stroke-width="0.8" opacity="0.5"/>
        <path d="M438,468 L258,340 L206,422" stroke="#fffaf6" stroke-width="1.15" opacity="0.55"/>
        <path d="M478,462 L704,418" stroke="#ffffff" stroke-width="1.15" opacity="0.55"/>
        <path d="M470,488 L652,496" stroke="#6f8088" stroke-width="0.9" opacity="0.65"/>
        <path d="M490,460 L624,372" stroke="#b7c4c6" stroke-width="0.7" opacity="0.75"/>
        <path d="M484,478 L646,430" stroke="#9aabae" stroke-width="0.7" opacity="0.7"/>
        <path d="M480,496 L618,488" stroke="#8aa0a6" stroke-width="0.7" opacity="0.6"/>
        <path d="M502,464 L622,390 L662,430 L612,496 L494,496" stroke="#a9b8bb" stroke-width="0.85" opacity="0.75"/>
        <path d="M560,448 L572,436" stroke="#fff" stroke-width="0.75" opacity="0.4"/>
        <path d="M630,436 L640,426" stroke="#6c7e86" stroke-width="0.75" opacity="0.55"/>
        <path d="M478,462 L655,348 L704,418" stroke="#f7fffd" stroke-width="1.05" opacity="0.4"/>
        <path d="M442,468 L498,498" stroke="#c9b8a4" stroke-width="0.7" opacity="0.8"/>
        <path d="M488,456 L424,506" stroke="#b7a894" stroke-width="0.7" opacity="0.75"/>
        <path d="M456,478 L468,520" stroke="#a89886" stroke-width="0.65" opacity="0.6"/>
        <path d="M436,486 L508,492" stroke="#fff" stroke-width="0.7" opacity="0.35"/>
        <path d="M456,462 C498,400 536,328 578,276" stroke="#fffaf6" stroke-width="1.35" opacity="0.7"/>
        <path d="M472,474 C508,414 546,342 590,290" stroke="#b5a694" stroke-width="0.9" opacity="0.75"/>
        <path d="M464,468 C506,406 544,334 584,282" stroke="#e7d8c6" stroke-width="0.7" opacity="0.9"/>
        <path d="M566,276 L572,294" stroke="#8c7b6a" stroke-width="0.9"/>
        <path d="M604,254 L632,270" stroke="#fff" stroke-width="0.9" opacity="0.8"/>
        <path d="M600,272 L632,270" stroke="#a89480" stroke-width="0.7"/>
        <path d="M606,286 L632,270" stroke="#cbbba8" stroke-width="0.6" opacity="0.8"/>
        <path d="M451,514 C396,556 350,600 308,646" stroke="#fffaf4" stroke-width="0.9" opacity="0.55"/>
        <path d="M456,522 C402,562 358,604 322,646" stroke="#7f9096" stroke-width="0.8" opacity="0.7"/>
      </g>
    </g>
  </defs>

  <rect width="900" height="1000" fill="url(#bg)"/>
  <rect width="900" height="1000" fill="url(#spot)"/>
  <rect width="900" height="1000" fill="url(#vignette)"/>
  <rect x="0" y="720" width="900" height="280" fill="url(#floor)"/>
  <line x1="80" y1="758" x2="820" y2="758" stroke="#c5d5e4" stroke-width="1" opacity="0.22"/>

  <g class="shadow">
    <ellipse cx="450" cy="804" rx="230" ry="30" fill="#000" opacity="0.32" filter="url(#contactWide)"/>
    <ellipse cx="450" cy="792" rx="156" ry="13" fill="#000" opacity="0.5" filter="url(#contact)"/>
  </g>
  <ellipse cx="450" cy="812" rx="150" ry="20" fill="url(#floorReflect)" opacity="0.85"/>

  <g class="float">
    <circle cx="450" cy="450" r="300" fill="#b7cbe0" opacity="0.2" filter="url(#haloBlur)"/>
    <g clip-path="url(#orb)">
      <circle cx="450" cy="450" r="300" fill="url(#interior)"/>
      <circle cx="450" cy="450" r="300" fill="url(#transmit)"/>
      <ellipse cx="418" cy="630" rx="86" ry="28" fill="#e7f4ff" opacity="0.4" filter="url(#soft)"/>
      <ellipse cx="478" cy="652" rx="46" ry="14" fill="#ffffff" opacity="0.55" filter="url(#soft)"/>
      <ellipse cx="390" cy="600" rx="24" ry="8" fill="#fff4e4" opacity="0.35" filter="url(#soft)"/>
      <use href="#crane" xlink:href="#crane" filter="url(#craneShadow)"/>
      <use href="#crane" xlink:href="#crane" filter="url(#paperGrain)"/>
      <g mask="url(#rimMask)">
        <use href="#crane" xlink:href="#crane" transform="translate(6,3) translate(450,450) scale(1.055) translate(-450,-450)" filter="url(#disperse)" opacity="0.55"/>
        <use href="#crane" xlink:href="#crane" transform="translate(-5,2) translate(450,450) scale(1.03) translate(-450,-450)" filter="url(#redFringe)" opacity="0.4"/>
      </g>
      <circle cx="450" cy="450" r="274" fill="none" stroke="#061018" stroke-width="18" opacity="0.32"/>
      <circle cx="450" cy="450" r="300" fill="url(#veil)" style="mix-blend-mode:soft-light"/>
      <g fill="none" stroke="#ffffff" stroke-linecap="round" opacity="0.14">
        <path d="M250,300 C300,230 430,210 560,250" stroke-width="22"/>
        <path d="M230,360 C340,300 520,310 690,390" stroke-width="8"/>
        <path d="M200,610 C340,650 560,640 730,590" stroke="#b9dcff" stroke-width="14" opacity="0.35"/>
      </g>
      <g fill="none" stroke="#fff" stroke-linecap="round">
        <path d="M188,470 C300,430 470,448 650,430 C720,424 760,450 770,490" stroke-width="7" opacity="0.07"/>
        <path d="M210,452 C330,418 500,428 700,468" stroke-width="1.6" opacity="0.28"/>
        <path d="M360,200 C300,280 290,390 320,470" stroke-width="1.1" opacity="0.16"/>
        <path d="M455,175 C500,280 515,420 490,560" stroke-width="1.1" opacity="0.14"/>
        <path d="M560,190 C610,300 640,430 620,560" stroke-width="1.1" opacity="0.12"/>
      </g>
      <circle cx="450" cy="450" r="300" fill="url(#fresnel)"/>
      <ellipse cx="450" cy="688" rx="100" ry="16" fill="#ffffff" opacity="0.1" filter="url(#soft)"/>
      <g class="spec">
        <g transform="rotate(-30 338 286)">
          <rect x="300" y="228" width="78" height="128" rx="18" fill="#ffffff" opacity="0.13" filter="url(#softBig)"/>
          <rect x="316" y="246" width="40" height="78" rx="12" fill="#ffffff" opacity="0.55"/>
          <rect x="326" y="258" width="16" height="48" rx="6" fill="#ffffff" opacity="0.9"/>
        </g>
        <ellipse cx="348" cy="300" rx="54" ry="28" transform="rotate(-36 348 300)" fill="url(#specGrad)" opacity="0.75"/>
        <circle cx="318" cy="268" r="3.2" fill="#ffffff"/>
        <circle cx="312" cy="262" r="1.3" fill="#ffffff"/>
      </g>
      <g transform="rotate(18 630 545)" opacity="0.3">
        <rect x="608" y="522" width="18" height="42" rx="5" fill="#ffffff"/>
      </g>
      <path d="M197,304 A292 292 0 0 0 304,197" fill="none" stroke="#ffffff" stroke-width="5" stroke-linecap="round" opacity="0.55"/>
      <path d="M214,318 A274 274 0 0 0 318,214" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" opacity="0.35"/>
      <path d="M292,700 A296 296 0 0 1 608,700" fill="none" stroke="#e7f4ff" stroke-width="3.5" stroke-linecap="round" opacity="0.38"/>
      <circle cx="446" cy="450" r="296" fill="none" stroke="#ff8b78" stroke-width="2" opacity="0.16"/>
      <circle cx="455" cy="451" r="296" fill="none" stroke="#79d0ff" stroke-width="2" opacity="0.2"/>
    </g>
    <circle cx="450" cy="450" r="303" fill="none" stroke="#05070a" stroke-width="4" opacity="0.28"/>
    <circle cx="450" cy="450" r="300" fill="none" stroke="url(#rimStroke)" stroke-width="1.7"/>
  </g>

  <line x1="286" y1="894" x2="362" y2="894" stroke="#93a3b4" stroke-width="0.8" opacity="0.75"/>
  <line x1="538" y1="894" x2="614" y2="894" stroke="#93a3b4" stroke-width="0.8" opacity="0.75"/>
  <text x="450" y="901" text-anchor="middle" font-family="Palatino Linotype, Palatino, Georgia, serif" font-size="22" letter-spacing="7" fill="#e6edf4">ORIZURU</text>
  <path d="M450,922 l4.5,5.5 l-4.5,5.5 l-4.5,-5.5 z" fill="none" stroke="#93a3b4" stroke-width="1"/>
  <text x="450" y="958" text-anchor="middle" font-family="Segoe UI, Helvetica, Arial, sans-serif" font-size="11" letter-spacing="3.5" fill="#9aa8b8">FOLDED PAPER · BLOWN GLASS</text>
</svg>
</body>
</html>

Test 2: Procedural Three.js globe

The second prompt required a canvas-generated continent texture, atmospheric glow, orbiting light particles, mouse-drag rotation, zoom, and one speed slider. Grok returned an 8.9 KB HTML file that loads Three.js 0.128.0 from jsDelivr and generates its surface and surrounding scene in JavaScript. It uses no external image assets. The output has one overlay slider and a full-window canvas.
In Chromium, the globe rendered, pointer drag changed its orientation, the wheel changed camera distance, and changing the slider updated its displayed value. The smoke test recorded no page errors or console errors. Visually, the result reads as a stylized alien globe: the land and ice forms are procedural color shapes, not geographically accurate continents. The bright particle ring and atmospheric edge make the scene more polished than a bare textured sphere. The CDN remains a runtime dependency, so opening it offline will not work unless Three.js is bundled locally.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Procedural Globe</title>
<style>
  html, body { margin: 0; height: 100%; background: #05070d; overflow: hidden; font-family: "Segoe UI", system-ui, sans-serif; color: #e8eef8; }
  canvas { display: block; width: 100%; height: 100%; }
  #ui {
    position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
    display: flex; align-items: center; gap: 14px;
    padding: 12px 18px; border-radius: 999px;
    background: rgba(8, 14, 28, 0.72); border: 1px solid rgba(160, 200, 255, 0.22);
    backdrop-filter: blur(10px); box-shadow: 0 10px 40px rgba(0,0,0,0.35);
    user-select: none;
  }
  #ui label { font-size: 13px; letter-spacing: 0.04em; opacity: 0.9; white-space: nowrap; }
  #speed { width: 180px; accent-color: #7eb6ff; cursor: pointer; }
  #val { min-width: 3.2em; font-variant-numeric: tabular-nums; font-size: 13px; color: #9ec4ff; }
</style>
</head>
<body>
<div id="ui">
  <label for="speed">Rotation speed</label>
  <input id="speed" type="range" min="0" max="2" step="0.01" value="0.35" aria-label="Globe rotation speed">
  <span id="val">0.35</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script>
(function () {
  var canvas = document.createElement("canvas");
  document.body.insertBefore(canvas, document.getElementById("ui"));

  var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true, alpha: false });
  renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
  renderer.setClearColor(0x05070d, 1);

  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
  camera.position.set(0, 0.35, 4.2);

  var globe = new THREE.Group();
  scene.add(globe);

  function hash(ix, iy) {
    var n = Math.sin(ix * 127.1 + iy * 311.7) * 43758.5453;
    return n - Math.floor(n);
  }
  function noise(x, y) {
    var ix = Math.floor(x), iy = Math.floor(y);
    var fx = x - ix, fy = y - iy;
    var ux = fx * fx * (3 - 2 * fx), uy = fy * fy * (3 - 2 * fy);
    var a = hash(ix, iy), b = hash(ix + 1, iy), c = hash(ix, iy + 1), d = hash(ix + 1, iy + 1);
    return a + (b - a) * ux + (c - a) * uy + (a - b - c + d) * ux * uy;
  }
  function fbm(x, y) {
    var v = 0, a = 0.5, f = 1;
    for (var i = 0; i < 6; i++) {
      v += a * noise(x * f, y * f);
      f *= 2; a *= 0.5;
    }
    return v;
  }

  function makeTexture() {
    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;
    for (var y = 0; y < h; y++) {
      var v = (y / (h - 1)) * Math.PI;
      var lat = Math.cos(v);
      for (var x = 0; x < w; x++) {
        var u = (x / w) * Math.PI * 2;
        var nx = Math.cos(u) * Math.sin(v);
        var ny = Math.cos(v);
        var nz = Math.sin(u) * Math.sin(v);
        var n = fbm(nx * 2.1 + 4.2, ny * 2.1 + nz * 1.7);
        n += 0.18 * fbm(nx * 6 + 1, nz * 6 + ny * 3);
        var ice = Math.abs(ny);
        var land = n > 0.52 && ice < 0.92;
        var coast = n > 0.49 && n <= 0.52;
        var r, g, b;
        if (ice > 0.86 && n > 0.46) {
          r = 230; g = 238; b = 246;
        } else if (land) {
          var elev = (n - 0.52) / 0.48;
          if (elev > 0.55) { r = 168; g = 160; b = 148; }
          else if (elev > 0.28) { r = 86; g = 128; b = 62; }
          else { r = 46; g = 110; b = 58; }
        } else if (coast) {
          r = 196; g = 186; b = 140;
        } else {
          var depth = 0.52 - n;
          r = 12 + depth * 20;
          g = 48 + (1 - depth) * 50;
          b = 110 + (1 - depth) * 70;
          r += lat * lat * 8;
        }
        var i = (y * w + x) * 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.wrapS = THREE.RepeatWrapping;
    tex.anisotropy = 4;
    return tex;
  }

  var sphere = new THREE.Mesh(
    new THREE.SphereGeometry(1, 96, 64),
    new THREE.MeshPhongMaterial({
      map: makeTexture(),
      specular: new THREE.Color(0x224466),
      shininess: 18
    })
  );
  globe.add(sphere);

  var atmo = new THREE.Mesh(
    new THREE.SphereGeometry(1.12, 64, 48),
    new THREE.ShaderMaterial({
      uniforms: {},
      vertexShader: [
        "varying vec3 vNormal;",
        "void main() {",
        "  vNormal = normalize(normalMatrix * normal);",
        "  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
        "}"
      ].join("\n"),
      fragmentShader: [
        "varying vec3 vNormal;",
        "void main() {",
        "  float f = pow(0.62 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.4);",
        "  gl_FragColor = vec4(0.35, 0.62, 1.0, 1.0) * f;",
        "}"
      ].join("\n"),
      side: THREE.BackSide,
      blending: THREE.AdditiveBlending,
      transparent: true,
      depthWrite: false
    })
  );
  globe.add(atmo);

  var RING = 420;
  var ringGeo = new THREE.BufferGeometry();
  var positions = new Float32Array(RING * 3);
  var colors = new Float32Array(RING * 3);
  for (var i = 0; i < RING; i++) {
    var a = (i / RING) * Math.PI * 2;
    var radius = 1.55 + (hash(i, 3) - 0.5) * 0.08;
    var tilt = 0.42;
    var y = Math.sin(a) * radius * Math.sin(tilt);
    var radFlat = Math.cos(Math.asin(Math.max(-1, Math.min(1, y / radius))));
    positions[i * 3] = Math.cos(a) * radius;
    positions[i * 3 + 1] = y;
    positions[i * 3 + 2] = Math.sin(a) * radius * Math.cos(tilt);
    var t = hash(i, 9);
    colors[i * 3] = 0.65 + t * 0.35;
    colors[i * 3 + 1] = 0.82 + t * 0.15;
    colors[i * 3 + 2] = 1.0;
  }
  ringGeo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  ringGeo.setAttribute("color", new THREE.BufferAttribute(colors, 3));
  var ring = new THREE.Points(ringGeo, new THREE.PointsMaterial({
    size: 0.035,
    vertexColors: true,
    transparent: true,
    opacity: 0.95,
    depthWrite: false,
    blending: THREE.AdditiveBlending
  }));
  scene.add(ring);

  var starsGeo = new THREE.BufferGeometry();
  var starPos = new Float32Array(600 * 3);
  for (var s = 0; s < 600; s++) {
    var th = hash(s, 1) * Math.PI * 2;
    var ph = Math.acos(2 * hash(s, 2) - 1);
    var rr = 18 + hash(s, 4) * 12;
    starPos[s * 3] = rr * Math.sin(ph) * Math.cos(th);
    starPos[s * 3 + 1] = rr * Math.cos(ph);
    starPos[s * 3 + 2] = rr * Math.sin(ph) * Math.sin(th);
  }
  starsGeo.setAttribute("position", new THREE.BufferAttribute(starPos, 3));
  scene.add(new THREE.Points(starsGeo, new THREE.PointsMaterial({
    color: 0xcfe0ff, size: 0.045, transparent: true, opacity: 0.85, depthWrite: false
  })));

  scene.add(new THREE.AmbientLight(0x334466, 0.55));
  var sun = new THREE.DirectionalLight(0xfff4e0, 1.15);
  sun.position.set(4, 2.2, 3);
  scene.add(sun);
  var fill = new THREE.DirectionalLight(0x4466aa, 0.35);
  fill.position.set(-3, -1, -2);
  scene.add(fill);

  var speed = 0.35;
  var slider = document.getElementById("speed");
  var valEl = document.getElementById("val");
  slider.addEventListener("input", function () {
    speed = parseFloat(slider.value);
    valEl.textContent = speed.toFixed(2);
  });

  var dragging = false;
  var lastX = 0, lastY = 0;
  var yaw = 0, pitch = 0.15;
  var dist = 4.2;

  canvas.addEventListener("pointerdown", function (e) {
    dragging = true;
    lastX = e.clientX; lastY = e.clientY;
    canvas.setPointerCapture(e.pointerId);
  });
  canvas.addEventListener("pointerup", function () { dragging = false; });
  canvas.addEventListener("pointercancel", function () { dragging = false; });
  canvas.addEventListener("pointermove", function (e) {
    if (!dragging) return;
    var dx = e.clientX - lastX, dy = e.clientY - lastY;
    lastX = e.clientX; lastY = e.clientY;
    yaw += dx * 0.005;
    pitch += dy * 0.005;
    pitch = Math.max(-1.2, Math.min(1.2, pitch));
  });
  canvas.addEventListener("wheel", function (e) {
    e.preventDefault();
    dist += e.deltaY * 0.0025;
    dist = Math.max(2.1, Math.min(8.5, dist));
  }, { passive: false });

  function resize() {
    var w = window.innerWidth, h = window.innerHeight;
    renderer.setSize(w, h, false);
    camera.aspect = w / Math.max(1, h);
    camera.updateProjectionMatrix();
  }
  window.addEventListener("resize", resize);
  resize();

  var clock = new THREE.Clock();
  function frame() {
    requestAnimationFrame(frame);
    var dt = Math.min(clock.getDelta(), 0.05);
    yaw += speed * dt;
    globe.rotation.y = yaw;
    globe.rotation.x = pitch;
    ring.rotation.y += dt * (0.35 + speed * 0.4);
    camera.position.set(0, 0.25, dist);
    camera.lookAt(0, 0, 0);
    renderer.render(scene, camera);
  }
  frame();
})();
</script>
</body>
</html>

Test 3: Wizard action-adventure game

The third prompt specified a low-poly wizard, floating pastel islands, WASD movement, jumping, drag camera, collisions, crystals, turrets, health and score HUD, sound, game over, and restart. I submitted that prompt to the Grok CLI. The request returned 429 Too Many Requests with a subscription:free-usage-exhausted message. No HTML was generated. I therefore cannot report whether Grok 4.7 could implement the game, and I have not inserted a substitute game into this article. A fair retest needs a fresh usage window and the same prompt, followed by browser checks of every game mechanic.

What this test supports

The two completed files demonstrate that this CLI model can produce substantial single-file visual experiences from these prompts. The SVG is the stronger visual composition; the globe is the stronger interaction test. Neither result establishes a general coding success rate, and the game failure reflects account quota rather than model capability. xAI's public model catalog should be used for current API availability and specifications; this article does not infer 4.7 pricing, context size, or benchmark scores from the CLI's model list.