It's good enough alright!
Here, ask it the rest:
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <title>Toyobi Field Animator — Direction · Intensity · Phase</title>
  <style>
    :root{
      --bg:#0b1020; --panel:#0f1724; --muted:#9aa4bf; --accent:#7ee3c7; --glass: rgba(255,255,255,0.04);
    }
    html,body{height:100%;margin:0;font-family:Inter,ui-sans-serif,system-ui,Arial;color:#e6eef8;background:linear-gradient(180deg,#071025 0%,#081220 60%);}
    .app{display:grid;grid-template-columns:360px 1fr;gap:18px;height:100vh;padding:18px;box-sizing:border-box}
    .panel{background:var(--panel);border-radius:12px;padding:14px;box-shadow:0 6px 24px rgba(2,6,23,0.6);overflow:auto}
    h1{font-size:16px;margin:0 0 10px;color:var(--accent)}
    label{display:block;font-size:12px;color:var(--muted);margin-top:10px}
    select,input[type=range],button{width:100%;margin-top:6px;background:var(--glass);border:1px solid rgba(255,255,255,0.04);color:#dbe9f8;padding:8px;border-radius:8px}
    .canvas-wrap{position:relative;border-radius:12px;overflow:hidden}
    #fieldCanvas{width:100%;height:100%;display:block;background:linear-gradient(180deg, rgba(10,14,22,0.6), rgba(2,4,10,0.2));}
    .controls-row{display:flex;gap:8px;margin-top:10px}
    .mini{flex:1}
    .legend{font-size:12px;color:var(--muted);margin-top:12px}
    .footer{font-size:12px;color:#8ea1c2;margin-top:12px}
    .stat{font-size:13px;color:#cfeee0;margin-top:8px}
    .badge{display:inline-block;padding:6px 8px;border-radius:8px;background:rgba(255,255,255,0.02);margin-right:6px}
    .top-row{display:flex;justify-content:space-between;align-items:center}
    .linkbtn{background:transparent;border:1px solid rgba(255,255,255,0.04);padding:6px 8px;border-radius:8px;color:var(--muted)}
    @media (max-width:900px){.app{grid-template-columns:1fr;grid-auto-rows:420px}}
  </style>
</head>
<body>
  <div class="app">
    <div class="panel">
      <div class="top-row">
        <h1>Toyobi Field Animator</h1>
        <button id="resetBtn" class="linkbtn">Reset</button>
      </div>
      <label for="stone">Stone</label>
      <select id="stone"></select>
      <label for="chemical">Chemical</label>
      <select id="chemical"></select>
      <label for="intensity">Global Intensity — scale</label>
      <input id="intensity" type="range" min="0" max="200" value="80">[Expand Post]
      <label for="frequency">Wave Frequency — Hz (visual)</label>
      <input id="frequency" type="range" min="0.1" max="6" step="0.1" value="1.2">
      <label for="phaseShift">Phase Shift — degrees</label>
      <input id="phaseShift" type="range" min="0" max="360" value="0">
      <div class="controls-row">
        <button id="playBtn">Pause</button>
        <button id="exportBtn">Export JSON</button>
      </div>
      <div class="legend">
        <div><span class="badge">Direction</span> crystal axis (vector)</div>
        <div><span class="badge">Intensity</span> vector length & opacity</div>
        <div><span class="badge">Phase</span> hue rotation and wave offset</div>
      </div>
      <div class="stat" id="statLine">Selected: —</div>
      <div class="footer">Click canvas to place a local probe. Hover vectors to see values.</div>
    </div>
    <div class="canvas-wrap panel" style="padding:0">
      <canvas id="fieldCanvas"></canvas>
    </div>
  </div>
  <script>
    // --------- Sample database (editable) ---------
    const STONES = {
      quartz: {name:'Quartz', axis:[0, -1], baseIntensity:1.0, notes:'piezoelectric; c-axis focal'},
      tourmaline: {name:'Tourmaline', axis:[0,-1], baseIntensity:0.9, notes:'strong polarization along long axis'},
      calcite: {name:'Calcite', axis:[1,0], baseIntensity:0.6, notes:'birefringent double-axis behavior'},
      hematite: {name:'Hematite', axis:[-1,0], baseIntensity:1.2, notes:'magnetic domain alignment'},
      malachite: {name:'Malachite', axis:[0.6,-0.8], baseIntensity:0.7, notes:'catalytic copper surface interactions'}
    };
    const CHEMICALS = {
      water: {name:'Water', perm:78, reactive:0.2, phaseMod:1.0},
      alcohol: {name:'Alcohol', perm:25, reactive:0.15, phaseMod:0.9},
      HCl: {name:'Hydrochloric Acid', perm:60, reactive:0.9, phaseMod:0.6},
      NaOH: {name:'Sodium Hydroxide', perm:50, reactive:0.85, phaseMod:0.7},
      NH3: {name:'Ammonia', perm:16, reactive:0.4, phaseMod:1.1}
    };
    // --------- Utilities ---------
    function $(id){return document.getElementById(id)}
    function lerp(a,b,t){return a+(b-a)*t}
    function clamp(v,a,b){return Math.max(a,Math.min(b,v))}
    // --------- UI population ---------
    const stoneSel = $('stone'); const chemSel = $('chemical');
    for(const k of Object.keys(STONES)){
      const o = document.createElement('option'); o.value=k; o.textContent = STONES[k].name; stoneSel.appendChild(o);
    }
    for(const k of Object.keys(CHEMICALS)){
      const o = document.createElement('option'); o.value=k; o.textContent = CHEMICALS[k].name; chemSel.appendChild(o);
    }
    // --------- Canvas setup ---------
    const canvas = $('fieldCanvas'); const ctx = canvas.getContext('2d');
    let W=0,H=0,paused=false, time=0;
    function resize(){W=canvas.width=canvas.clientWidth;H=canvas.height=canvas.clientHeight;}
    window.addEventListener('resize',resize); resize();
    // Local probes placed by clicks
    const probes = [];
    canvas.addEventListener('click',(e)=>{
      const r = canvas.getBoundingClientRect(); probes.push({x:e.clientX-r.left,y:e.clientY-r.top});
    });
    // --------- Field model ---------
    function fieldAt(x,y,stoneKey,chemKey,t){
      // Normalize coordinates to -1..1
      const nx = (x/W)*2-1; const ny = (y/H)*2-1;
      const stone = STONES[stoneKey]; const chem = CHEMICALS[chemKey];
      // Direction vector (unit) from stone axis
      let dir = stone.axis.slice(); const mag = Math.hypot(dir[0],dir[1])||1; dir = [dir[0]/mag, dir[1]/mag];
      // Intensity: base * global slider * distance falloff * chemical reactive modifier
      const globalI = Number($('intensity').value)/80; // normalized
      const dist = Math.hypot(nx-dir[0]*0.2, ny-dir[1]*0.2);
      const falloff = 1/(1+Math.pow(dist*3,2));
      const intensity = stone.baseIntensity * chem.reactive * globalI * falloff;
      // Phase: base wave from frequency, modulated by chemical phaseMod and user phaseShift
      const freq = Number($('frequency').value);
      const userPhase = Number($('phaseShift').value) * Math.PI/180;
      const phase = Math.sin( ( (nx*2 + ny*2) * freq * 0.4 ) + t*freq*0.7 + userPhase ) * chem.phaseMod;
      // Per-vector angle: base axis angle plus small local rotation from phase
      const baseAngle = Math.atan2(dir[1],dir[0]);
      const angle = baseAngle + 0.6*phase;
      return {vx:Math.cos(angle), vy:Math.sin(angle), intensity:clamp(intensity,0,2), phase, angle};
    }
    // --------- Draw primitives ---------
    function drawVector(x,y,vx,vy,intensity,phase){
      const len = 40 * intensity; const alpha = clamp(intensity*0.9,0.08,0.98);
      const hue = (180 + phase*80) % 360; // phase → hue shift
      ctx.save();
      ctx.translate(x,y);
      ctx.rotate(Math.atan2(vy,vx));
      // tail
      ctx.globalAlpha = alpha*0.9;
      ctx.lineWidth = 3; ctx.lineCap='round';
      ctx.strokeStyle = `hsla(${hue},80%,60%,${alpha})`;
      ctx.beginPath(); ctx.moveTo(-len*0.2,0); ctx.lineTo(len,0); ctx.stroke();
      // head
      ctx.beginPath(); ctx.moveTo(len,0); ctx.lineTo(len-10,-6); ctx.lineTo(len-10,6); ctx.closePath();
      ctx.fillStyle = `hsla(${hue},90%,60%,${alpha})`; ctx.fill();
      // glow
      ctx.beginPath(); ctx.arc(len*0.2,0,6+intensity*6,0,Math.PI*2); ctx.fillStyle = `rgba(200,240,210,${alpha*0.06})`; ctx.fill();
      ctx.restore();
    }
    function drawWave(x,y,t,intensity,phase){
      const amp = 10*intensity*(0.6+phase*0.4);
      ctx.beginPath();
      for(let i=0;i<120;i++){
        const px = x - 60 + (i/119)*120;
        const py = y + Math.sin((i*0.2) + t*1.8 + phase*2) * amp;
        if(i===0) ctx.moveTo(px,py); else ctx.lineTo(px,py);
      }
      ctx.lineWidth = 1.6; ctx.strokeStyle = `rgba(200,230,220,${0.06+intensity*0.12})`; ctx.stroke();
    }
    // --------- Animation loop ---------
    function frame(ts){
      if(!lastTS) lastTS = ts; const dt = (ts-lastTS)/1000; lastTS = ts; if(!paused) time += dt;
      ctx.clearRect(0,0,W,H);
      // subtle background grid
      ctx.save();
      ctx.fillStyle='rgba(255,255,255,0.02)'; ctx.fillRect(0,0,W,H);
      ctx.restore();
      const stoneKey = stoneSel.value; const chemKey = chemSel.value;
      // draw vector field grid
      const nx = 9, ny = 7; for(let iy=0;iy<ny;iy++){
        for(let ix=0;ix<nx;ix++){
          const x = (ix+0.5)/nx*W; const y = (iy+0.5)/ny*H;
          const f = fieldAt(x,y,stoneKey,chemKey,time);
          drawVector(x,y,f.vx,f.vy,f.intensity,f.phase);
          // subtle local wave near center area
          if(ix=Math.floor(nx/2) && iy=Math.floor(ny/2)) drawWave(x,y+40,time,f.intensity,f.phase);
        }
      }
      // Draw probes
      for(const p of probes){
        const f = fieldAt(p.x,p.y,stoneKey,chemKey,time);
        drawVector(p.x,p.y,f.vx,f.vy,f.intensity,f.phase);
        ctx.fillStyle='rgba(255,255,255,0.02)'; ctx.beginPath(); ctx.arc(p.x,p.y,26,0,Math.PI*2); ctx.fill();
        ctx.fillStyle='rgba(255,255,255,0.04)'; ctx.fillRect(p.x-40,p.y+32,80,40);
        ctx.fillStyle='rgba(200,240,210,0.95)'; ctx.font='12px monospace'; ctx.fillText(`I:${f.intensity.toFixed(2)} P:${f.phase.toFixed(2)}`,p.x-36,p.y+52);
      }
      // UI Stat update
      const stone = STONES[stoneKey]; const chem = CHEMICALS[chemKey];
      $('statLine').textContent = `Selected: ${stone.name} ↔ ${chem.name} · baseI=${stone.baseIntensity.toFixed(2)} · perm=${chem.perm}`;
      requestAnimationFrame(frame);
    }
    let lastTS=0; requestAnimationFrame(frame);
    // --------- Controls ---------
    $('playBtn').addEventListener('click',()=>{ paused=!paused; $('playBtn').textContent = paused? 'Play':'Pause'; });
    $('resetBtn').addEventListener('click',()=>{ probes.length=0; $('intensity').value=80; $('frequency').value=1.2; $('phaseShift').value=0; });
    $('exportBtn').addEventListener('click',()=>{
      const data = {stone:stoneSel.value, chemical:chemSel.value, intensity: $('intensity').value, frequency:$('frequency').value, phaseShift:$('phaseShift').value, probes};
      const blob = new Blob([JSON.stringify(data,null,2)],{type:'application/json'});
      const url = URL.createObjectURL(blob); const a=document.createElement('a'); a.href=url; a.download='toyobi-field.json'; a.click(); URL.revokeObjectURL(url);
    });
    // Initialize defaults
    stoneSel.value='quartz'; chemSel.value='water';
    // ensure canvas fills its container on load
    function fitCanvas(){ canvas.style.width = '100%'; canvas.style.height = '100%'; resize(); }
    fitCanvas();
    // small accessibility: keyboard to toggle play/pause
    window.addEventListener('keydown',(e)=>{ if(e.key===' ') { e.preventDefault(); $('playBtn').click(); } });
    // Tooltip for hover - show vector values
    canvas.addEventListener('mousemove',(e)=>{
      const r = canvas.getBoundingClientRect(); const x=e.clientX-r.left, y=e.clientY-r.top;
      // no heavy compute: sample nearby grid point
      const f = fieldAt(x,y,stoneSel.value,chemSel.value,time);
      canvas.title = `angle=${(f.angle*180/Math.PI).toFixed(0)}° · intensity=${f.intensity.toFixed(2)} · phase=${f.phase.toFixed(2)}`;
    });
  </script>
</body>
</html>
Same thing index.html and browser