Workout Exercise Wheel adds variety by selecting from movements that have already been approved for the participant. It should never assign an exercise that conflicts with health advice, pain, injury, equipment, space, supervision, or ability.
Wheel of Names
Elimination: OFFAccumulation: OFFAuto-Spin: OFFFair Rotation: OFF
Add entries and spin the wheel.
(()=>{
const r=document.currentScript.closest(‘.wnx-spinner’);
if(!r||r.dataset.ready)return;
r.dataset.ready=’1′;
const $=s=>r.querySelector(s),$$=s=>[…r.querySelectorAll(s)];
const params=new URLSearchParams(location.search),cfg=document.querySelector(‘.wnx-config’);
const mode=cfg?.dataset.mode||r.dataset.mode||’standard’;
const title=cfg?.dataset.title||r.dataset.title||document.querySelector(‘.wn-page h1′)?.textContent||’Wheel of Names’;
const raw=params.get(‘wnitems’)||cfg?.dataset.items||r.dataset.items||’Alice~Bob~Charlie~David~Eve~Frank’;
const palettes=[
((cfg?.dataset.palette)||r.dataset.palette||”).split(‘,’).filter(Boolean),
[‘#6366f1′,’#8b5cf6′,’#ec4899′,’#f97316′,’#14b8a6′,’#3b82f6’],
[‘#ef476f’,’#f78c6b’,’#ffd166′,’#06d6a0′,’#118ab2′,’#7353ba’],
[‘#22c55e’,’#06b6d4′,’#3b82f6′,’#8b5cf6′,’#ec4899′,’#f59e0b’]
].filter(x=>x.length>2);
let colors=palettes[0];
let entries=raw.split(‘~’).map(v=>{try{return decodeURIComponent(v)}catch(e){return v}}).filter(Boolean).map(v=>{
const p=v.split(‘|’);
const weight=mode===’weighted’?Math.max(1,Number(p.pop())||1):1;
return{name:p.join(‘|’),weight,count:0,last:-999,image:”};
});
let angle=0,busy=false,history=[],undo=[],redo=[],spinNumber=0,lastReceipt=null,preSpin=[];
let backgroundImage=”,centerImage=”,backgroundOpacity=.45,centerRadius=76,pendingEntryImage=”,pendingEntryFilename=”;
const imageCache=new Map();
const canvas=$(‘.wnx-canvas’),ctx=canvas.getContext(‘2d’),spin=$(‘.wnx-spin’),result=$(‘.wnx-result’),list=$(‘.wnx-list’);
const elimination=$(‘.wnx-elimination’),accumulation=$(‘.wnx-accumulation’),auto=$(‘.wnx-auto’),winnerCount=$(‘.wnx-winner-count’),duration=$(‘.wnx-duration’);
const balance=$(‘.wnx-smart-balance’),cooldown=$(‘.wnx-smart-cooldown’);
$(‘.wnx-title’).textContent=title;
elimination.checked=mode===’norepeat’||params.get(‘wnremove’)===’1′;
winnerCount.value=mode===’multi’?Math.max(1,Number(cfg?.dataset.count||r.dataset.count)||3):1;
$(‘.wnx-multi-field’).classList.toggle(‘wnx-hidden’,mode!==’multi’);
const escapeHtml=s=>String(s).replace(/[&”‘]/g,m=>({‘&’:’&’,”:’>’,'”‘:’"’,”‘”:’'’}[m]));
const snapshot=()=>JSON.stringify(entries);
const save=()=>{undo.push(snapshot());if(undo.length>25)undo.shift();redo=[];};
const randomInt=n=>{if(n=limit);return a[0]%n;};
function getImage(src){
if(!src)return null;
if(imageCache.has(src))return imageCache.get(src);
const img=new Image();
img.onload=draw;
img.src=src;
imageCache.set(src,img);
return img;
}
function drawCover(img,x,y,w,h){
if(!img||!img.complete||!img.naturalWidth)return;
const scale=Math.max(w/img.naturalWidth,h/img.naturalHeight);
const dw=img.naturalWidth*scale,dh=img.naturalHeight*scale;
ctx.drawImage(img,x+(w-dw)/2,y+(h-dh)/2,dw,dh);
}
function readOptimizedImage(file,maxSize=1000){
return new Promise((resolve,reject)=>{
if(!file||!file.type.startsWith(‘image/’)){reject(new Error(‘Please choose an image file.’));return;}
const reader=new FileReader();
reader.onerror=()=>reject(new Error(‘The image could not be read.’));
reader.onload=()=>{
const img=new Image();
img.onerror=()=>reject(new Error(‘The image format is not supported.’));
img.onload=()=>{
const scale=Math.min(1,maxSize/Math.max(img.naturalWidth,img.naturalHeight));
const w=Math.max(1,Math.round(img.naturalWidth*scale)),h=Math.max(1,Math.round(img.naturalHeight*scale));
const temp=document.createElement(‘canvas’);temp.width=w;temp.height=h;
const t=temp.getContext(‘2d’);t.drawImage(img,0,0,w,h);
const type=file.type===’image/png’?’image/png’:’image/jpeg’;
resolve(temp.toDataURL(type,type===’image/jpeg’?.86:undefined));
};
img.src=String(reader.result||”);
};
reader.readAsDataURL(file);
});
}
const smartOn=()=>!elimination.checked&&(balance.checked||Number(cooldown.value)>0);
function eligibleEntries(){
let pool=[…entries];
if(!smartOn())return pool;
const cd=Number(cooldown.value);
if(cd>0){const cooled=pool.filter(e=>spinNumber-e.last>=cd);if(cooled.length)pool=cooled;}
if(balance.checked&&pool.length){const min=Math.min(…pool.map(e=>e.count));pool=pool.filter(e=>e.count===min);}
return pool.length?pool:[…entries];
}
function segments(){
const total=entries.reduce((s,e)=>s+e.weight,0)||1;
let used=0;
return entries.map(entry=>{const start=used/total*Math.PI*2;used+=entry.weight;return{entry,start,end:used/total*Math.PI*2};});
}
function draw(){
const size=900,center=450,radius=418,segs=segments(),allowed=new Set(eligibleEntries());
ctx.clearRect(0,0,size,size);
if(backgroundImage){
const img=getImage(backgroundImage);
ctx.save();ctx.beginPath();ctx.arc(center,center,radius,0,Math.PI*2);ctx.clip();ctx.globalAlpha=backgroundOpacity;drawCover(img,center-radius,center-radius,radius*2,radius*2);ctx.restore();
}
if(!segs.length){
ctx.beginPath();ctx.arc(center,center,radius,0,Math.PI*2);ctx.fillStyle=’#0b1628′;ctx.fill();ctx.strokeStyle=’rgba(129,140,248,.24)’;ctx.lineWidth=6;ctx.stroke();ctx.fillStyle=’#94a3b8′;ctx.font=’800 34px system-ui’;ctx.textAlign=’center’;ctx.fillText(‘Add entries’,center,center);return;
}
segs.forEach((seg,index)=>{
const dim=smartOn()&&!allowed.has(seg.entry);
const start=seg.start+angle,end=seg.end+angle;
ctx.save();
ctx.globalAlpha=dim?.2:1;
ctx.beginPath();ctx.moveTo(center,center);ctx.arc(center,center,radius,start,end);ctx.closePath();
if(seg.entry.image){
ctx.save();ctx.clip();drawCover(getImage(seg.entry.image),center-radius,center-radius,radius*2,radius*2);ctx.restore();
ctx.globalAlpha=dim?.12:.22;ctx.fillStyle=colors[index%colors.length];ctx.fill();ctx.globalAlpha=dim?.2:1;
}else{
ctx.fillStyle=colors[index%colors.length];
if(backgroundImage)ctx.globalAlpha=dim?.14:.72;
ctx.fill();ctx.globalAlpha=dim?.2:1;
}
ctx.strokeStyle=’rgba(255,255,255,.82)’;ctx.lineWidth=5;ctx.stroke();
const mid=(seg.start+seg.end)/2+angle;
ctx.translate(center,center);ctx.rotate(mid);ctx.textAlign=’right’;ctx.fillStyle=’#fff’;ctx.shadowColor=’rgba(0,0,0,.55)’;ctx.shadowBlur=8;
ctx.font=`850 ${Math.max(18,Math.min(33,350/entries.length+16))}px system-ui`;
const label=seg.entry.name.length>22?seg.entry.name.slice(0,20)+’…’:seg.entry.name;
ctx.fillText(label,radius-34,10);ctx.restore();
});
ctx.save();ctx.beginPath();ctx.arc(center,center,centerRadius,0,Math.PI*2);ctx.clip();
if(centerImage){drawCover(getImage(centerImage),center-centerRadius,center-centerRadius,centerRadius*2,centerRadius*2);}else{ctx.fillStyle=’#0b1628′;ctx.fillRect(center-centerRadius,center-centerRadius,centerRadius*2,centerRadius*2);}
ctx.restore();ctx.beginPath();ctx.arc(center,center,centerRadius,0,Math.PI*2);ctx.strokeStyle=’rgba(255,255,255,.24)’;ctx.lineWidth=10;ctx.stroke();
}
function updateBadges(){
[[ $(‘.wnx-badge-elimination’),elimination,’Elimination’],[$(‘.wnx-badge-accumulation’),accumulation,’Accumulation’],[$(‘.wnx-badge-auto’),auto,’Auto-Spin’],[$(‘.wnx-badge-smart’),{checked:smartOn()},’Fair Rotation’]].forEach(([el,input,label])=>{el.textContent=`${label}: ${input.checked?’ON’:’OFF’}`;el.classList.toggle(‘is-on’,input.checked);});
}
function balanceScore(){
if(!entries.length)return 100;
const total=entries.reduce((s,e)=>s+e.count,0),mean=total/entries.length;
if(!mean)return 100;
const deviation=entries.reduce((s,e)=>s+Math.abs(e.count-mean),0)/entries.length;
return Math.max(0,Math.round(100-deviation/mean*100));
}
function updateDashboard(){
$(‘.wnx-balance-score’).textContent=balanceScore()+’%’;$(‘.wnx-smart-spins’).textContent=String(spinNumber);
$(‘.wnx-smart-counts’).innerHTML=[…entries].sort((a,b)=>a.count-b.count||a.name.localeCompare(b.name)).map(e=>`
${escapeHtml(e.name)}${e.count}
`).join(”)||’
No entries yet.
‘;
const status=$(‘.wnx-smart-status’);
if(elimination.checked&&(balance.checked||Number(cooldown.value)>0))status.textContent=’Smart Fair Rotation is paused while Elimination mode is active.’;
else if(!smartOn())status.textContent=’Pure random mode is active.’;
else{const parts=[];if(balance.checked)parts.push(‘least-selected priority’);if(Number(cooldown.value)>0)parts.push(`${cooldown.value}-spin cooldown`);status.textContent=`Smart mode active: ${parts.join(‘ + ‘)}. Dimmed slices are temporarily ineligible.`;}
}
function render(){
list.innerHTML=entries.map((entry,index)=>`
A Workout Exercise Wheel randomly chooses one active movement. A qualified person should prepare the exercise pool, technique, duration, intensity, rest, and alternatives before anyone spins.
How to use the Workout Exercise Wheel
Replace the example entries
Add the exact choices that meet your activity rules, or keep the prepared list when it already matches your goal.
Review the active list and mode
Remove unavailable entries, check duplicates, and choose standard or elimination according to whether repeats are allowed.
Spin and confirm the result
Press Spin the Wheel, read the text result, and record it when another person or later task depends on the outcome.
Best uses for this Workout Exercise Wheel
Warm-up variety
Choose among gentle movements already suitable for the planned session.
Home workout circuits
Randomize the next safe exercise, interval, or mobility drill.
PE and group activities
Select an accessible station or movement after the teacher reviews every option.
Fitness games
Add a playful choice mechanic without turning pain or exhaustion into a challenge.
Workout Exercise Wheel setup tips
Get medical or professional advice when needed.
Include regressions, alternatives, and rest.
Remove movements requiring unavailable equipment or supervision.
Stop for pain, dizziness, breathing difficulty, or other concerning symptoms.
Responsible use: A random wheel is suitable for low-risk choices and prepared activities. It should not replace consent, safety checks, accessibility adjustments, professional judgment, or legal requirements.