Community widgets

A creation from the community

The Ninth Apple

Made by ArkheOS

Spot the badly disguised wolf among nine spinning illustrated apples. Catch the furry clue, reveal the impostor, and play another round.

Make your version

Play this widget, then change its rules, look, or purpose with Widget Canvas in ChatGPT.

Open Widget Canvas in ChatGPT

Copy the selected request. In ChatGPT, choose “Try in chat,” replace the example prompt, and paste it.

You are exploring the published defaults. Changes here stay in this browser and do not change the creator’s work.

Follow the remix chain

  1. Nine Spinning Apples
    Made by ArkheOS
  2. The Ninth Apple
    Made by ArkheOS
View published code

HTML

<main class="toy"><header><p class="eyebrow">AN ORCHARD WITH ONE SMALL PROBLEM</p><h1>The Ninth Apple</h1><p class="intro">Nine apples. Eight are telling the truth.</p><div class="scorebar"><span>Round <b id="round">1</b></span><span>Score <b id="score">0</b></span></div></header><section class="stage" aria-label="Catch an apple"><div class="stage-note" id="stage-note">FIRST: CATCH THE APPLE</div><div id="orchard"></div><div class="reveal" id="reveal" hidden><svg viewBox="0 0 160 130" aria-hidden="true"><ellipse cx="82" cy="119" rx="48" ry="7" fill="#80603a" opacity=".18"/><path d="M104 94Q145 76 134 59Q151 84 120 108Z" fill="#777d83" stroke="#414b52" stroke-width="2"/><path d="M58 78Q39 101 59 117L103 117Q121 101 102 78Z" fill="#777d83" stroke="#414b52" stroke-width="2"/><path d="M67 89Q78 83 94 89L97 116H65Z" fill="#dbd9c8"/><path d="M48 56L45 13L71 39M92 39L118 13L115 59" fill="#777d83" stroke="#414b52" stroke-width="3" stroke-linejoin="round"/><path d="M51 24L54 48L65 39M110 24L97 40L108 47" fill="#c69383"/><path d="M47 55Q46 36 80 36Q116 36 117 57L127 70L103 78Q80 97 56 77L35 68Z" fill="#899198" stroke="#414b52" stroke-width="2"/><path d="M58 66Q80 58 101 66L94 80Q80 91 65 79Z" fill="#e5dfcd"/><ellipse cx="80" cy="68" rx="8" ry="5" fill="#374148"/><path d="M70 79Q80 87 91 79" fill="none" stroke="#414b52" stroke-width="2"/><path d="M60 52L68 55M93 55L102 52" stroke="#414b52" stroke-width="3" stroke-linecap="round"/><circle cx="65" cy="59" r="2.5" fill="#26333a"/><circle cx="97" cy="59" r="2.5" fill="#26333a"/><path d="M79 84L84 84L82 94Z" fill="#d78378"/><g class="shell left-shell"><path d="M42 67Q7 52 10 83Q12 112 43 111L52 85Z" fill="#d9543b" stroke="#9d3529" stroke-width="2"/><path d="M18 80Q17 69 28 67" stroke="#ffe6bb" stroke-width="4" fill="none" stroke-linecap="round"/></g><g class="shell right-shell"><path d="M113 67Q149 52 149 83Q146 114 115 111L108 85Z" fill="#df6943" stroke="#9d3529" stroke-width="2"/></g><path d="M75 33Q77 23 83 20" stroke="#68492c" stroke-width="4" fill="none"/><path d="M83 28Q91 14 104 23Q99 32 83 28" fill="#426f45"/></svg><strong>“I am a very normal fruit.”</strong><span id="reward"></span></div><div class="stage-bottom"><span id="count">1 / 9</span><span id="caught">0 caught</span></div></section><p id="status" role="status">First, tap the apple to catch it.</p><div class="buttons"><button id="add" aria-label="Add one apple">+</button><button id="advance">Bring in nine</button><button id="pause">Hold still</button></div><p class="keyhint">Tap / Enter to catch · arrows to move · 1–9 to choose</p><div class="speed"><label for="speed">Spin speed <output id="speed-value">1.0×</output></label><input id="speed" type="range" min="0" max="3" step=".1" value="1"></div><footer><button id="undo" disabled>Undo last guess</button><button id="reset">Reset</button><span id="save">Saved · silent</span></footer><div id="confirm" hidden role="group" aria-label="Confirm reset"><p>Reset score and start the guided round again?</p><button id="yes">Yes, reset</button><button id="no">Keep playing</button></div><p class="motion" id="motion"></p><p class="credit">Original apples: <a href="https://widgets.arkheos.ai/community/widgets/5f3551f1-b277-4a51-99e8-032f640819ca" target="_blank" rel="noopener">Nine Spinning Apples · ArkheOS</a></p></main>

CSS

*{box-sizing:border-box}body{margin:0;background:#f8f1e2;color:#362f25;font-family:ui-sans-serif,system-ui,sans-serif}.toy{max-width:520px;margin:auto;padding:24px 20px 16px;background:#faf4e7;border:1px solid #e6dac4;border-radius:24px;overflow:hidden}header{margin-bottom:18px}.eyebrow{font-size:9px;letter-spacing:1.65px;font-weight:750;color:#776349;margin:0 0 10px}h1{font-family:Georgia,serif;font-size:clamp(26px,6.6vw,34px);letter-spacing:-1.2px;font-weight:normal;margin:0;line-height:1.15}.intro{font-size:13px;color:#776956;margin:9px 0 0}.stage{position:relative;height:310px;border:1px solid #e5d8bf;border-radius:18px;background:radial-gradient(ellipse at 50% 38%,#fffcf3,#eee1c9);overflow:hidden}.stage-note{position:absolute;top:14px;width:100%;text-align:center;font-size:8px;letter-spacing:1.6px;color:#8a785f}#orchard{position:absolute;inset:39px 9px 35px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);align-items:center;justify-items:center}.apple{position:relative;width:80px;height:76px;border:0;background:transparent;padding:0;cursor:pointer;touch-action:manipulation;isolation:isolate}.apple svg{width:74px;height:74px;display:block;margin:auto;overflow:visible;will-change:transform}.apple .shadow{position:absolute;bottom:4px;left:17px;width:46px;height:8px;border-radius:50%;background:#70563227;filter:blur(2px);z-index:-1}.apple .badge{position:absolute;right:1px;bottom:0;background:#fbf5e7;border:1px solid #d8c8aa;border-radius:50%;font-size:10px;font-weight:700;width:19px;height:19px;display:grid;place-items:center}.apple[aria-pressed=true]{border-radius:50%;outline:1px dashed #8c7856;outline-offset:-3px}.apple[aria-pressed=true] .badge{background:#426543;color:#fff;border-color:#426543}.stage-bottom{position:absolute;bottom:13px;left:17px;right:17px;display:flex;justify-content:space-between;color:#79674e;font-size:11px;font-variant-numeric:tabular-nums}#count{font-weight:700}.action{display:flex;align-items:center;gap:15px;margin:19px 0 20px}#add{flex:0 0 64px;width:64px;height:64px;border-radius:50%;background:#bd4533;color:#fff8e9;border:1px solid #993629;box-shadow:0 5px 0 #903324,0 8px 14px #75352120;font-size:42px;line-height:1;cursor:pointer;touch-action:manipulation}#add:active{transform:translateY(4px);box-shadow:0 1px 0 #903324}#add:disabled{background:#806f53;border-color:#806f53;box-shadow:0 4px 0 #5f533f;cursor:default;font-size:27px}.action strong{font-family:Georgia,serif;font-size:18px;font-weight:normal}.action p{font-size:12px;color:#7b6650;line-height:1.5;margin:5px 0 0}.speed label{display:flex;justify-content:space-between;font-size:12px;font-weight:650}.speed output{font-weight:normal;color:#79674e}input[type=range]{width:100%;height:32px;margin:7px 0 0;accent-color:#a64130;cursor:pointer;touch-action:pan-y}.range-labels{display:flex;justify-content:space-between;font-size:10px;color:#8a775e}footer{display:flex;align-items:center;gap:8px;border-top:1px solid #e6d9c1;margin-top:18px;padding-top:12px}footer button{min-height:40px;padding:0 13px;border:1px solid #d8c8ad;border-radius:9px;background:#fff9ec;color:#55432e;font-size:12px;cursor:pointer}button:disabled{opacity:.55}footer span{margin-left:auto;font-size:10px;color:#867257}.motion{font-size:10px;color:#79674e;margin:8px 0 0}.motion:empty{display:none}button:focus-visible,input:focus-visible{outline:3px solid #395f54;outline-offset:3px}.arrival{animation:arrive .55s cubic-bezier(.2,.8,.3,1.2)}.burst{position:absolute;inset:5px;border:2px solid #c89848;border-radius:50%;pointer-events:none;animation:burst .65s ease-out forwards}@keyframes arrive{from{opacity:0;transform:scale(.25) translateY(15px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes burst{from{opacity:.9;transform:scale(.55)}to{opacity:0;transform:scale(1.45)}}@media(max-width:350px){.toy{padding:20px 14px 14px;border-radius:18px}.apple{width:76px}.action{gap:12px}.action strong{font-size:17px}footer span{font-size:9px}.eyebrow{letter-spacing:1.2px}}@media(prefers-reduced-motion:reduce){.arrival,.burst{animation:none}.burst{display:none}#add:active{transform:none}}
[hidden]{display:none!important}.toy{max-width:480px}.scorebar{display:flex;justify-content:space-between;font-size:12px;color:#79674e;margin-top:14px}.scorebar b{color:#3d5940;font-size:18px}.stage{height:325px}#orchard{inset:38px 10px 32px}.apple{width:78px;height:80px}.apple svg{width:70px;height:70px}.apple .clue{opacity:0}.apple.clue-on .clue{opacity:1}.apple.caught{outline:2px dashed #6a8151;border-radius:50%;outline-offset:-2px}.apple.caught .badge{background:#426543;color:white}.apple:active{background:#eedbb1;border-radius:50%}#status{font-size:13px;line-height:1.5;min-height:59px;color:#62513b;margin:13px 0 9px}.buttons{display:flex;gap:9px;align-items:center;margin-bottom:8px}.buttons #add{flex:0 0 51px;width:51px;height:51px;font-size:33px}.buttons button:not(#add){min-height:45px;padding:7px 12px;border:1px solid #d6c2a2;background:#fff9ec;border-radius:10px;color:#57442d;font-size:12px}#advance{flex:1;font-weight:650;background:#426543!important;color:#fff5dc!important}.keyhint{font-size:10px;color:#837055;margin:10px 0 17px}.reveal{position:absolute;inset:35px 10px 32px;background:#fbf3e0;display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:3;border-radius:16px}.reveal svg{width:220px;max-width:85%;height:175px}.reveal strong{font:18px Georgia,serif;color:#544431;text-align:center}.reveal span{font-size:12px;color:#426543;font-weight:750;margin-top:7px}.shell{animation:peel .6s ease-out}.left-shell{transform-origin:52px 85px}.right-shell{transform-origin:108px 85px}@keyframes peel{from{transform:scale(.1);opacity:0}to{transform:scale(1);opacity:1}}#confirm{background:#efe0c4;padding:12px;border-radius:12px;margin-top:10px;font-size:12px}#confirm p{margin:0 0 8px}#confirm button{min-height:40px;border:1px solid #bda480;border-radius:8px;background:#fff7e5;padding:7px 12px;color:#5a4430;margin-right:7px}.credit{font-size:9px;color:#8a7659;line-height:1.5;margin:10px 0 0}.credit a{color:#536e45}@media(max-width:350px){.apple{width:74px}.buttons{gap:7px}.buttons button:not(#add){padding:6px 8px;font-size:11px}footer{gap:6px}footer button{padding:0 9px;font-size:11px}footer span{font-size:9px}.keyhint{font-size:9px}}@media(prefers-reduced-motion:reduce){.shell{animation:none}}

JavaScript

const Ninth=(()=>{
const copy=x=>JSON.parse(JSON.stringify(x));
function fresh(){return {count:1,speed:1,score:0,round:1,phase:'intro',guided:true,wolf:-1,miss:0,caught:Array(9).fill(false),order:[0,1,2,3,4,5,6,7,8],seed:19381,paused:false,message:'First, tap the apple to catch it.'};}
function rng(s){s.seed=(Math.imul(s.seed,1664525)+1013904223)>>>0;return s.seed/4294967296;}
function start(s){s.phase='hunt';s.wolf=s.guided?4:Math.floor(rng(s)*9);s.caught=Array(9).fill(false);s.miss=0;s.message=s.guided?'That furry ear is a clue. Catch the badly disguised apple!':'Watch for an ear or tail. Catch the impostor.';}
function add(s,all=false){if(s.phase==='won'||s.count===9)return;s.count=all?9:s.count+1;if(s.count===9)start(s);else{s.phase='build';s.message='Add apples until there are nine. Tap to catch or release a real apple.';}}
function guess(s,id){if(id<0||id>=s.count||s.phase==='won')return null;
if(s.phase!=='hunt'){s.caught[id]=!s.caught[id];if(s.phase==='intro'){s.phase='build';s.message='Caught! Now choose Bring in nine to meet the impostor.';}else s.message='Apple '+(s.order.indexOf(id)+1)+(s.caught[id]?' caught. Tap to release.':' released.');return 'catch';}
if(s.caught[id]){s.caught[id]=false;s.message='Real apple released. The wolf stays hidden.';return 'release';}
if(id===s.wolf){const earned=Math.max(25,100-s.miss*15);s.score=Math.min(999999,s.score+earned);s.phase='won';s.caught[id]=true;s.message='Not an apple. Just a wolf with terrible wardrobe choices! +'+earned+' points.';return 'win';}
s.caught[id]=true;s.miss=Math.min(999,s.miss+1);let candidates=s.order.filter(x=>x!==s.wolf&&!s.caught[x]);if(!candidates.length){s.caught=Array(9).fill(false);candidates=s.order.filter(x=>x!==s.wolf);}const old=s.wolf,next=candidates[Math.floor(rng(s)*candidates.length)],a=s.order.indexOf(old),b=s.order.indexOf(next);[s.order[a],s.order[b]]=[s.order[b],s.order[a]];s.wolf=old;s.message='A real apple! The wolf swapped places. Watch again.';return 'wrong';}
function next(s){s.round=Math.min(9999,s.round+1);s.guided=false;s.count=9;start(s);}
function valid(s){return s&&Number.isInteger(s.count)&&s.count>=1&&s.count<=9&&Number.isFinite(s.speed)&&s.speed>=0&&s.speed<=3&&Number.isInteger(s.score)&&s.score>=0&&s.score<=999999&&Number.isInteger(s.round)&&s.round>=1&&s.round<=9999&&['intro','build','hunt','won'].includes(s.phase)&&typeof s.guided==='boolean'&&typeof s.paused==='boolean'&&Number.isInteger(s.wolf)&&s.wolf>=-1&&s.wolf<9&&(!['hunt','won'].includes(s.phase)||s.count===9&&s.wolf>=0)&&Number.isInteger(s.miss)&&s.miss>=0&&s.miss<=999&&Array.isArray(s.caught)&&s.caught.length===9&&s.caught.every(x=>typeof x==='boolean')&&Array.isArray(s.order)&&s.order.length===9&&new Set(s.order).size===9&&s.order.every(x=>Number.isInteger(x)&&x>=0&&x<9)&&Number.isInteger(s.seed)&&s.seed>=0&&s.seed<=4294967295&&typeof s.message==='string'&&s.message.length<250;}
return {fresh,add,guess,next,valid,copy};
})();
(()=>{
'use strict';const colors=[['#e76142','#a92f29'],['#e8b440','#bd7026'],['#93ab54','#547340'],['#d74d55','#972c42'],['#ef8f48','#bb4d2d'],['#bfd073','#739447'],['#b55343','#763327'],['#f0bd70','#cd7b39'],['#c54137','#852e2c']];
function svg(i){const [a,b]=colors[i],uid='apple'+i;const bulge=i%3===0?3:i%3===1?-2:0;return '<svg aria-hidden="true" viewBox="0 0 100 100"><defs><linearGradient id="'+uid+'" x1="0" y1="0" x2="1" y2="1"><stop stop-color="'+a+'"/><stop offset="1" stop-color="'+b+'"/></linearGradient></defs><path d="M50 29 Q46 18 52 10" fill="none" stroke="#68492c" stroke-width="5" stroke-linecap="round"/><path d="M51 23 Q58 4 78 13 Q74 31 51 23Z" fill="'+(i%2?'#668344':'#426f45')+'" stroke="#365337" stroke-width="1.5"/><path d="M54 23 72 15" stroke="#d4dea1" stroke-width="1.4" fill="none"/><path d="M50 33 C'+(30+bulge)+' 18 12 33 15 57 C17 80 32 94 47 86 Q50 84 53 86 C69 94 85 77 86 55 C88 32 69 19 50 33Z" fill="url(#'+uid+')" stroke="'+b+'" stroke-width="1.5"/><path d="M24 49 Q23 36 36 33" fill="none" stroke="#fff4d2" opacity=".8" stroke-width="5" stroke-linecap="round"/><ellipse cx="24" cy="58" rx="2" ry="3" fill="#fff7db" opacity=".55"/><path d="M59 79 Q75 73 77 60" fill="none" stroke="'+b+'" stroke-width="4" opacity=".5" stroke-linecap="round"/><path d="M44 84 Q50 81 56 84" fill="none" stroke="'+b+'" stroke-width="2"/></svg>';}

const $=id=>document.getElementById(id),bridge=typeof WidgetCanvas==='undefined'?null:WidgetCanvas,ID='the-ninth-apple-v1',reduce=matchMedia('(prefers-reduced-motion: reduce)'),slots=[4,3,5,1,7,0,8,2,6];
let s=Ninth.fresh(),undo=null,angles=Array(9).fill(0),nodes=[],last=0,clock=0,clueWas=false,confirmPaused=false;
function packet(){return {schema:1,workId:ID,current:Ninth.copy(s),lastGuess:undo?Ninth.copy(undo):null};}
function save(){if(!bridge)return;try{Promise.resolve(bridge.setState(packet())).catch(()=>{$('save').textContent='Save unavailable · silent';});Promise.resolve(bridge.setContext({workId:ID,title:'The Ninth Apple',parentPublicationId:'5f3551f1-b277-4a51-99e8-032f640819ca',phase:s.phase,count:s.count,score:s.score,round:s.round})).catch(()=>{});}catch(e){$('save').textContent='Save unavailable · silent';}}
function restore(q){if(!q||q.workId!==ID||!Ninth.valid(q.current))return;s=Ninth.copy(q.current);undo=q.lastGuess&&Ninth.valid(q.lastGuess)?Ninth.copy(q.lastGuess):null;clock=0;draw();}
function ui(){$('round').textContent=s.round;$('score').textContent=s.score;$('status').textContent=s.message;$('count').textContent=s.count+' / 9';$('caught').textContent=s.caught.slice(0,s.count).filter(Boolean).length+' caught';$('speed').value=s.speed;$('speed-value').textContent=s.speed.toFixed(1)+'×';$('undo').disabled=!undo;$('add').disabled=s.count===9||s.phase==='won';$('advance').textContent=s.phase==='won'?'Next round':'Bring in nine';$('advance').disabled=s.phase==='hunt'||s.phase==='intro';$('pause').textContent=s.paused?'Let spin':'Hold still';$('stage-note').textContent=s.phase==='intro'?'FIRST: CATCH THE APPLE':s.phase==='hunt'?(s.guided?'FIND THE FURRY EAR':'WATCH FOR AN EAR OR TAIL'):s.phase==='won'?'AN APPLE-SOLUTELY BAD DISGUISE':'MAKE ROOM FOR NINE';$('reveal').hidden=s.phase!=='won';$('orchard').setAttribute('aria-hidden',String(s.phase==='won'));$('reward').textContent='Caught! '+Math.max(25,100-s.miss*15)+' points';$('motion').textContent=reduce.matches?'Reduced motion: apples stay still and the furry clue stays visible.':'';}
function disguise(id){const ear='<g class="clue"><path d="M62 32L71 9L84 34Z" fill="#818b90" stroke="#465159" stroke-width="2"/><path d="M70 28L72 17L79 29Z" fill="#d4a494"/><path d="M82 65Q108 52 94 43Q116 55 88 82" fill="#818b90" stroke="#465159" stroke-width="2"/><path d="M93 50L99 56L94 60" fill="#e4dfce"/></g>';
return svg(id).replace('</svg>',s.phase==='hunt'&&s.wolf===id?ear+'</svg>':'</svg>');}
function draw(focusSlot=-1){$('orchard').replaceChildren();nodes=[];for(let pos=0;pos<s.count;pos++){const id=s.order[pos],b=document.createElement('button');b.className='apple'+(s.caught[id]?' caught':'');b.style.gridArea=(Math.floor(slots[pos]/3)+1)+' / '+(slots[pos]%3+1);b.dataset.pos=pos;b.setAttribute('aria-label','Apple '+(pos+1)+(s.caught[id]?', caught. Release.':'. Catch.'));b.setAttribute('aria-pressed',String(s.caught[id]));b.disabled=s.phase==='won';b.innerHTML='<span class="shadow"></span>'+disguise(id)+'<span class="badge">'+(pos+1)+'</span>';b.onclick=()=>guess(pos);$('orchard').append(b);nodes.push({button:b,svg:b.querySelector('svg'),id,pos});}ui();clueWas=false;paint();if(focusSlot>=0&&s.phase!=='won')nodes[focusSlot]?.button.focus();if(s.phase==='won')$('advance').focus();}
function guess(pos){if(confirmPaused||s.phase==='won')return;undo=Ninth.copy(s);const result=Ninth.guess(s,s.order[pos]);clock=0;draw(pos);if(result==='wrong')$('status').textContent=s.message;save();}
function paint(){const clue=s.phase==='hunt'&&(s.guided||reduce.matches||s.paused||s.speed===0||clock%3.6<.95);for(const n of nodes){n.svg.style.transform='rotate('+angles[n.id]+'deg)';const visible=clue&&n.id===s.wolf;n.button.classList.toggle('clue-on',visible);const label='Apple '+(n.pos+1)+(visible?', a furry ear or tail is visible. Catch.':s.caught[n.id]?', caught. Release.':'. Catch.');if(n.button.getAttribute('aria-label')!==label)n.button.setAttribute('aria-label',label);}
if(clue&&!clueWas&&s.phase==='hunt')$('status').textContent=s.message+' A furry clue at apple '+(s.order.indexOf(s.wolf)+1)+'.';
if(!clue&&clueWas)$('status').textContent=s.message;clueWas=clue;}
$('add').onclick=()=>{undo=null;Ninth.add(s);draw();save();};
$('advance').onclick=()=>{undo=null;if(s.phase==='won')Ninth.next(s);else Ninth.add(s,true);clock=0;draw();save();};
$('pause').onclick=()=>{s.paused=!s.paused;ui();paint();save();};
$('speed').oninput=()=>{s.speed=Number($('speed').value);ui();paint();save();};
$('undo').onclick=()=>{if(!undo)return;s=undo;undo=null;clock=0;draw();save();};
function closeConfirm(){$('confirm').hidden=true;confirmPaused=false;$('reset').focus();}
$('reset').onclick=()=>{$('confirm').hidden=false;confirmPaused=true;$('no').focus();};
$('no').onclick=closeConfirm;$('yes').onclick=()=>{s=Ninth.fresh();undo=null;angles.fill(0);clock=0;closeConfirm();draw(0);save();};
document.addEventListener('keydown',e=>{if(e.key==='Escape'&&confirmPaused){closeConfirm();return;}if(confirmPaused||e.target.matches('input'))return;if(/^[1-9]$/.test(e.key)&&!e.repeat){const pos=Number(e.key)-1;if(pos<s.count){e.preventDefault();guess(pos);}}if(e.target.matches('.apple')&&e.key.startsWith('Arrow')){e.preventDefault();const pos=Number(e.target.dataset.pos),d=e.key==='ArrowLeft'?-1:e.key==='ArrowRight'?1:e.key==='ArrowUp'?-3:3;nodes[(pos+d+s.count*3)%s.count]?.button.focus();}});
function frame(t){const dt=last?Math.min(.05,(t-last)/1000):0;last=t;if(!document.hidden&&!confirmPaused){clock+=dt;if(!s.paused&&!reduce.matches&&s.phase!=='won')for(let i=0;i<s.count;i++)if(!s.caught[i])angles[i]=(angles[i]+dt*s.speed*(35+i*4)*(i%2?-1:1))%360;paint();}requestAnimationFrame(frame);}
window.addEventListener('widgetcanvas:restore',e=>restore(e.detail));document.addEventListener('visibilitychange',()=>{last=0;if(document.hidden)save();});reduce.addEventListener('change',()=>{angles.fill(0);ui();paint();});
draw();if(bridge)try{bridge.setControlsPosition('top-right');bridge.setResetEnabled(false);bridge.setSharingEnabled(false);const q=bridge.getState();if(q&&typeof q.then==='function')q.then(restore).catch(()=>{});else restore(q);}catch(e){$('save').textContent='Save unavailable · silent';}requestAnimationFrame(frame);
})();