The Ninth Apple
Made by ArkheOS
A remix with visible ancestry
A creation from the community
Made by ArkheOS
Add up to nine hand-drawn spinning apples, change their speed, tap to catch each one, and undo or reset your changes.
Play this widget, then change its rules, look, or purpose with 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.
Made by ArkheOS
A remix with visible ancestry
Made by ArkheOS
A remix with visible ancestry
<main class="toy"><header><p class="eyebrow">A SMALL EXERCISE IN IMAGINATION</p><h1>Nine Spinning Apples</h1><p class="intro">Can you picture all nine at once?</p></header><section class="stage" aria-label="Apple stage"><div class="stage-note">ONE THOUGHT, THEN ANOTHER</div><div id="orchard"></div><div class="stage-bottom"><span id="count">1 / 9</span><span id="caught">0 caught</span></div></section><div class="action"><button id="add" aria-label="Add one apple"><span aria-hidden="true">+</span></button><div><strong id="invitation">Make room for another.</strong><p id="status" role="status" aria-live="polite">Tap an apple to catch it.</p></div></div><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="0.1" value="1"><div class="range-labels"><span>Still</span><span>Whirl</span></div></div><footer><button id="undo" disabled>Undo</button><button id="reset">Reset</button><span id="save">Saved as you play</span></footer><p class="motion" id="motion"></p></main>
*{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}}(()=>{
'use strict';
const ID='nine-spinning-apples-original-v1',bridge=typeof WidgetCanvas==='undefined'?null:WidgetCanvas;
const $=id=>document.getElementById(id),clone=x=>JSON.parse(JSON.stringify(x));
let s={count:1,speed:1,caught:Array(9).fill(false),angles:Array(9).fill(0)},history=[],nodes=[],angles=s.angles.slice(),last=0;
const reduce=matchMedia('(prefers-reduced-motion: reduce)');
const colors=[['#e76142','#a92f29'],['#e8b440','#bd7026'],['#93ab54','#547340'],['#d74d55','#972c42'],['#ef8f48','#bb4d2d'],['#bfd073','#739447'],['#b55343','#763327'],['#f0bd70','#cd7b39'],['#c54137','#852e2c']];
const positions=[4,3,5,1,7,0,8,2,6];
function valid(q){return q&&Number.isInteger(q.count)&&q.count>=1&&q.count<=9&&typeof q.speed==='number'&&Number.isFinite(q.speed)&&q.speed>=0&&q.speed<=3&&Array.isArray(q.caught)&&q.caught.length===9&&q.caught.every(x=>typeof x==='boolean')&&Array.isArray(q.angles)&&q.angles.length===9&&q.angles.every(x=>Number.isFinite(x)&&Math.abs(x)<=360);}
function snapshot(){return {...clone(s),angles:angles.map(x=>((x%360)+360)%360)};}
function save(){const packet={schema:1,workId:ID,current:snapshot(),undo:history.slice(-30)};if(bridge){try{Promise.resolve(bridge.setState(packet)).catch(()=>{$('save').textContent='Save unavailable';});Promise.resolve(bridge.setContext({workId:ID,title:'Nine Spinning Apples',kind:'original complete interactive toy',count:s.count,speed:s.speed,caught:s.caught.slice(0,s.count)})).catch(()=>{});}catch(e){$('save').textContent='Save unavailable';}}}
function restore(packet){if(!packet||packet.workId!==ID||!valid(packet.current))return;s=clone(packet.current);angles=s.angles.slice();history=Array.isArray(packet.undo)?packet.undo.filter(valid).slice(-30).map(clone):[];draw();$('status').textContent='Your apples are back.';}
function remember(){history.push(snapshot());history=history.slice(-30);}
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>';}
function draw(arrival=-1){$('orchard').replaceChildren();nodes=[];for(let i=0;i<s.count;i++){const b=document.createElement('button');b.className='apple';b.style.gridArea=(Math.floor(positions[i]/3)+1)+' / '+(positions[i]%3+1);b.setAttribute('aria-pressed',String(s.caught[i]));b.setAttribute('aria-label','Apple '+(i+1)+': '+(s.caught[i]?'caught. Release':'spinning. Catch'));b.innerHTML='<span class="shadow"></span>'+svg(i)+'<span class="badge">'+(i+1)+'</span>';b.querySelector('svg').style.transform='rotate('+angles[i]+'deg)';b.onclick=()=>{remember();s.caught[i]=!s.caught[i];b.setAttribute('aria-pressed',String(s.caught[i]));b.setAttribute('aria-label','Apple '+(i+1)+': '+(s.caught[i]?'caught. Release':'spinning. Catch'));update();$('status').textContent='Apple '+(i+1)+(s.caught[i]?' caught. Tap to release.':' released. Let it turn.');save();};if(i===arrival){b.classList.add('arrival');const ring=document.createElement('span');ring.className='burst';b.append(ring);setTimeout(()=>{b.classList.remove('arrival');ring.remove();},700);}$('orchard').append(b);nodes.push(b.querySelector('svg'));}update();}
function update(){$('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)+'×';$('add').disabled=s.count===9;$('add').querySelector('span').textContent=s.count===9?'✓':'+';$('invitation').textContent=s.count===9?'Nine. Hold that thought.':'Make room for another.';$('undo').disabled=!history.length;$('motion').textContent=reduce.matches?'Reduced motion: apples stay still. Catch and release still work.':'';}
$('add').onclick=()=>{if(s.count===9)return;remember();const i=s.count++;s.caught[i]=false;angles[i]=i*27;draw(i);$('status').textContent=s.count===9?'All nine are here. Can you catch them all?':s.count+' distinct apples. Tap one to catch it.';save();};
let speedStart=null;$('speed').addEventListener('input',()=>{if(speedStart===null){speedStart=snapshot();history.push(speedStart);history=history.slice(-30);}s.speed=Number($('speed').value);update();$('status').textContent=s.speed===0?'A still life.':reduce.matches?'Speed saved for when motion is enabled.':'Turning at '+s.speed.toFixed(1)+'×.';save();});$('speed').addEventListener('change',()=>{speedStart=null;});
$('undo').onclick=()=>{if(!history.length)return;s=history.pop();angles=s.angles.slice();speedStart=null;draw();$('status').textContent='Undone. Your previous thought is back.';save();};
$('reset').onclick=()=>{remember();s={count:1,speed:1,caught:Array(9).fill(false),angles:Array(9).fill(0)};angles=s.angles.slice();speedStart=null;draw();$('status').textContent='One apple again. Undo brings the others back.';save();};
function frame(t){const dt=last?Math.min((t-last)/1000,.05):0;last=t;if(!document.hidden&&!reduce.matches&&s.speed>0){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;nodes[i].style.transform='rotate('+angles[i]+'deg)';}}requestAnimationFrame(frame);}
reduce.addEventListener('change',update);document.addEventListener('visibilitychange',()=>{last=0;if(document.hidden)save();});
window.addEventListener('widgetcanvas:restore',e=>restore(e.detail));
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';}}
requestAnimationFrame(frame);
})();