Instantly share code, notes, and snippets.
Created
February 27, 2026 23:37
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save EncodeTheCode/5418b75023469560a32cb9e8eaf8145e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width,initial-scale=1" /> | |
| <title>PS Classic Menu — Fixed Options Enter</title> | |
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | |
| <style> | |
| :root{ --selected-size:200px; } | |
| html,body{height:100%;margin:0;background:radial-gradient(circle at center,#111 0%,#000 100%);color:#eee;font-family:system-ui, -apple-system, "Segoe UI", Roboto, Arial;} | |
| .stage{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;} | |
| .carousel{position:relative;width:900px;height:500px;pointer-events:none;will-change:transform;} | |
| .game{ | |
| --size:100px; | |
| position:absolute; left:50%; top:50%; | |
| transform:translate(-50%,-50%); | |
| width:var(--size); height:var(--size); | |
| display:flex; align-items:center; justify-content:center; | |
| pointer-events:auto; cursor:pointer; | |
| border-radius:0.35em; overflow:hidden; | |
| border:1px solid rgba(255,255,255,0.06); | |
| background:linear-gradient(180deg,#151515,#0b0b0f); | |
| box-shadow:0 8px 20px rgba(0,0,0,0.65); | |
| transition: | |
| transform 420ms cubic-bezier(.25,.8,.25,1), | |
| width 420ms cubic-bezier(.25,.8,.25,1), | |
| height 420ms cubic-bezier(.25,.8,.25,1), | |
| opacity 260ms ease, | |
| box-shadow 260ms ease, | |
| border-color 200ms ease; | |
| will-change: transform; | |
| } | |
| .game img{ | |
| width:100%; height:100%; object-fit:cover; display:block; border-radius:inherit; pointer-events:none; backface-visibility:hidden; | |
| } | |
| .game.front{ | |
| border:1px solid #fff; | |
| box-shadow:0 14px 36px rgba(0,0,0,0.6), 0 0 18px rgba(255,255,255,0.06); | |
| } | |
| .game.single{ z-index:9999!important; } | |
| .game.single img{ width:var(--selected-size); height:var(--selected-size); border-radius:0.35em; box-shadow:0 28px 68px rgba(0,0,0,0.75); } | |
| .controls{position:absolute;top:16px;left:50%;transform:translateX(-50%);color:#ddd;font-size:13px;background:rgba(255,255,255,0.02);padding:8px 12px;border-radius:8px;border:1px solid rgba(255,255,255,0.03);display:flex;gap:12px;align-items:center;pointer-events:auto;} | |
| .arrowBtn{background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.04);color:#ddd;padding:8px 10px;border-radius:8px;cursor:pointer;} | |
| .hud{position:absolute;bottom:26px;width:100%;text-align:center;color:#ddd;font-size:14px;pointer-events:none;} | |
| .game-title{position:absolute;bottom:86px;left:50%;transform:translateX(-50%);color:#bfbfbf;font-size:18px;letter-spacing:1.6px;text-transform:uppercase;pointer-events:none;text-align:center;width:min(900px,94vw);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0.98;} | |
| @media (max-width:900px){ .carousel{width:94vw;height:46vh;} .game-title{font-size:14px;bottom:70px;} } | |
| .game:focus{outline:none;} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="stage"> | |
| <div class="carousel" id="carousel" aria-hidden="false"></div> | |
| <div class="controls" aria-hidden="false"> | |
| <div class="menuLabel" id="menuTitle">Main Menu</div> | |
| <div style="display:flex;gap:10px;align-items:center;"> | |
| <button class="arrowBtn" id="btnLeft" aria-label="Move left">A / ←</button> | |
| <button class="arrowBtn" id="btnRight" aria-label="Move right">D / →</button> | |
| </div> | |
| </div> | |
| <div class="game-title" id="gameTitle" aria-live="polite" aria-atomic="true"></div> | |
| <div class="hud" id="hintText">Press <strong>A</strong>/<strong>D</strong> or ←/→ to rotate, <strong>Enter</strong> to select, <strong>R</strong> to return</div> | |
| </div> | |
| <script> | |
| let spacing=0.69,perspectiveTilt=0.32,baseOffset=227,spiralStrength=1.15,boomerangIntensity=0.9,stage1Duration=820,stage2Duration=520; | |
| const mainGames=[{title:'Crash Bandicoot',image:'https://upload.wikimedia.org/wikipedia/en/4/44/Crash_Bandicoot_Cover.png'},{title:'Spyro the Dragon',color:'#2f6fb2',image:'https://upload.wikimedia.org/wikipedia/en/5/5b/Spyro_the_Dragon_cover.png'},{title:'Tekken 3',color:'#2fb280'},{title:'Final Fantasy VII',color:'#b28b2f'},{title:'Metal Gear Solid',color:'#8a2fb2'},{title:'Ridge Racer',color:'#b22f6f'},{title:'Tombi!',color:'#2fb29e'},{title:'Barbie: Race & Ride',color:'#6fb22f'},{title:'Barbie Explorer',color:'#ff97fd'},{title:'Wipeout 2097',color:'#b24c2f'},{title:'Worms Armageddon',color:'#2fb2b2'},{title:'Tomb Raider 3',color:'#b24ca8'},{title:'NHL 2001',color:'#ea1daa'},{title:'Crash Bash',color:'#ab4ec4'},{title:'Crash Team Racing',color:'#f1edde'},{title:'Grand Theft Auto 2',color:'#ae7d15'},{title:'Army Men: Omega Soldier',color:'#7e1ed3'},{title:'Croc 2',color:'#ed8a2e'},{title:'Pac-Man World',color:'#aae4ea'},{title:"Hello Kitty's Cube Frenzy",color:'#842aa3'},{title:'Syphon Filter',color:'#bc784a'},{title:'Syphon Filter 2',color:'#bc784a'},{title:'Syphon Filter 3',color:'#bc784a'},{title:'Muppet Monster Adventure',color:'#d74a83'},{title:'Muppet RaceMania',color:'#d74a83'},{title:'Resident Evil',color:'#ff1c2b'},{title:'Alundra',color:'#ffe57e'},{title:'Alundra 2',color:'#ffe57e'},{title:'Ape Escape',color:'#59ffbe'},{title:'Options',color:'#4c6fb2'}]; | |
| const optionsGames=[{title:'Display Settings',color:'#3f7fb2'},{title:'Audio Settings',color:'#5fb27f'},{title:'Controls',color:'#b27f3f'},{title:'Network',color:'#b25f9a'},{title:'System Info',color:'#7f8fb2'},{title:'Back',color:'#4c6fb2'}]; | |
| function escapeForSVG(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');} | |
| function makeSVGData(title,color){const safe=escapeForSVG(title);const svg=`<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400'><defs><linearGradient id='g' x1='0' x2='1'><stop offset='0' stop-color='${color}'/><stop offset='1' stop-color='#111'/></linearGradient></defs><rect width='100%' height='100%' fill='url(#g)'/><text x='50%' y='50%' font-family='Verdana, Arial, sans-serif' font-size='34' fill='#fff' dominant-baseline='middle' text-anchor='middle'>${safe}</text></svg>`;return 'data:image/svg+xml;utf8,'+encodeURIComponent(svg);} | |
| function cubicBezierPoint(p0,p1,p2,p3,t){const u=1-t;const x=u*u*u*p0.x+3*u*u*t*p1.x+3*u*t*t*p2.x+t*t*t*p3.x;const y=u*u*u*p0.y+3*u*u*t1*p1.y+3*u*t*t*p2.y+t*t*t*p3.y;return {x,y};} | |
| function easeOutCubic(x){return 1-Math.pow(1-x,3);} | |
| function signedAngle(raw){while(raw<=-Math.PI)raw+=2*Math.PI;while(raw>Math.PI)raw-=2*Math.PI;return raw;} | |
| const $carousel=$('#carousel');let menuData=mainGames.slice(),nodes=[],N=0,selected=0,currentMenu='main',menuStack=[],animating=false,inSingleView=false,savedSelected=null; | |
| function getLayoutParams(){const rect=$carousel[0].getBoundingClientRect();const baseRX=Math.min(rect.width,900)*0.42;const baseRY=Math.min(rect.height,500)*0.34;const rX=baseRX*spacing;const rY=baseRY*spacing;return {rX,rY,width:rect.width,height:rect.height};} | |
| function angleStepFor(n){return 2*Math.PI/n;} | |
| function sizeFromD(d){if(d<=0.5){const t=d/0.5;return 110-(110-80)*t;}else{const t=(d-0.5)/0.5;return 80-(80-55)*t;}} | |
| function buildMenu(menuArray){ | |
| $carousel.empty();nodes=[]; | |
| menuArray.forEach((g,i)=>{ | |
| const el=document.createElement('div');el.className='game';el.dataset.index=i;el.setAttribute('role','button');el.setAttribute('tabindex','0'); | |
| const img=document.createElement('img');img.alt=g.title;img.src=makeSVGData(g.title.replace(/&/g,'&'),g.color||'#444');el.appendChild(img);$carousel.append(el);nodes.push(el); | |
| }); | |
| N=nodes.length; | |
| nodes.forEach((node,idx)=>{ | |
| node.addEventListener('click',()=>{if(inSingleView){restoreFromSingleView().then(()=>{selected=idx;updatePositions();updateGameTitle();});return;}selected=idx;updatePositions();updateGameTitle();}); | |
| node.addEventListener('keydown',(ev)=>{if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();if(!animating&&!inSingleView){selected=Number(node.dataset.index);handleEnterOnSelected();}}}); | |
| }); | |
| } | |
| function circlePosFor(i,sel,count,params){const step=angleStepFor(count);const raw=signedAngle((i-sel)*step);const d=Math.abs(raw)/Math.PI;const size=sizeFromD(d);const x=Math.sin(raw)*params.rX;const y=Math.cos(raw)*params.rY*perspectiveTilt+baseOffset;const scale=size/110;return {x,y,scale,raw,d,size};} | |
| function updatePositions(){ | |
| if(inSingleView||!nodes.length)return; | |
| const params=getLayoutParams();let frontIndex=null; | |
| for(let i=0;i<N;i++){ | |
| const node=nodes[i];const pos=circlePosFor(i,selected,N,params); | |
| node.style.setProperty('--size',pos.size+'px');node.style.width=pos.size+'px';node.style.height=pos.size+'px'; | |
| node.style.transform=`translate(-50%,-50%) translate(${pos.x}px,${pos.y}px) scale(${pos.scale}) rotate(0deg)`; | |
| node.style.zIndex=Math.round((1-pos.d)*1000);node.style.opacity=1; | |
| if(Math.abs(pos.raw)<(angleStepFor(N)*0.5)){node.classList.add('front');node.setAttribute('aria-current','true');frontIndex=i;}else{node.classList.remove('front');node.removeAttribute('aria-current');} | |
| } | |
| if(frontIndex===null&&nodes[selected]){nodes[selected].classList.add('front');nodes[selected].setAttribute('aria-current','true');frontIndex=selected;} | |
| try{if(frontIndex!==null&&nodes[frontIndex])nodes[frontIndex].focus();}catch(e){} | |
| updateGameTitle(); | |
| } | |
| function animateEntrance(durationOverride){if(!nodes.length)return;const params=getLayoutParams();const targets=[];for(let i=0;i<N;i++){const pos=circlePosFor(i,selected,N,params);targets.push(pos);const node=nodes[i];node.style.transform=`translate(-50%,-50%) translate(0px,0px) scale(0.12) rotate(0deg)`;node.style.opacity=0;node.classList.remove('single');}nodes.forEach(n=>$(n).stop(true,true));animating=true;const dur=durationOverride||900;$({t:0}).animate({t:1},{duration:dur,easing:'swing',step(now){const p=now;const e=easeOutCubic;for(let i=0;i<N;i++){const node=nodes[i],tar=targets[i];const spin=(1-p)*(3*2*Math.PI);const angle=tar.raw+spin;const x=Math.sin(angle)*tar.x*e(p);const y=Math.cos(angle)*tar.y*e(p);const scale=0.12+(tar.scale-0.12)*e(p);node.style.transform=`translate(-50%,-50%) translate(${x}px,${y}px) scale(${scale}) rotate(0deg)`;node.style.opacity=Math.min(1,p*1.05);node.style.zIndex=tar.size;}},complete(){animating=false;updatePositions();try{nodes[selected].focus();}catch(e){}}});} | |
| /* ------------------- ENTER / OPTIONS HANDLING ------------------- */ | |
| function handleEnterOnSelected(){ | |
| const item=menuData[selected];if(!item)return; | |
| const realTitle=String(item.title).replace(/&/g,'&').trim().toLowerCase(); | |
| if(realTitle==='options'){openOptions();return;} | |
| if(realTitle==='back'){returnToParent();return;} | |
| enterSelect(); | |
| } | |
| function openOptions(){if(animating)return;transitionToMenu(optionsGames,'Options',0);} | |
| function returnToParent(){const prev=menuStack.pop();if(!prev){animating=false;return;}transitionToMenu(prev.data.slice(),prev.title||'Main Menu',prev.selectedIndex||0);} | |
| function enterSelect(){if(animating||inSingleView)return $.Deferred().resolve().promise();animating=true;inSingleView=true;savedSelected=selected;nodes.forEach(n=>$(n).stop(true,true));const chosen=nodes[selected];const img=chosen.querySelector('img');const curSize=parseFloat(getComputedStyle(chosen).getPropertyValue('--size'))||100;nodes.forEach((node,idx)=>{if(idx===selected)return;$(node).animate({opacity:0},260).promise().done(()=>{node.style.pointerEvents='none';});});const dfd=$.Deferred();$({t:0}).animate({t:1},{duration:420,easing:'swing',step(now){const p=now;const curScale=(curSize/110);const targetScale=(parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--selected-size'))||200)/110;const scale=curScale+(targetScale-curScale)*p;chosen.style.transform=`translate(-50%,-50%) translate(0px,0px) scale(${scale})`;chosen.style.zIndex=9999;chosen.style.opacity=1;if(img){const sizePx=(curSize*(1-p))+((parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--selected-size'))||200)*p);img.style.width=sizePx+'px';img.style.height=sizePx+'px';}},complete(){chosen.classList.add('single');animating=false;dfd.resolve();}});return dfd.promise();} | |
| function restoreFromSingleView(){if(!inSingleView&&!animating)return $.Deferred().resolve().promise();animating=true;nodes.forEach(n=>$(n).stop(true,true));const promises=nodes.map(node=>{const d=$.Deferred();node.style.pointerEvents='auto';$(node).animate({opacity:1},240,function(){const img=node.querySelector('img');if(img){img.style.width='';img.style.height='';}d.resolve();});return d.promise();});return $.when(...promises).then(()=>{nodes.forEach(n=>{n.classList.remove('single');n.style.zIndex='';});inSingleView=false;animating=false;if(savedSelected!==null){selected=savedSelected;savedSelected=null;}setTimeout(updatePositions,20);});} | |
| /* ------------------- NAVIGATION ------------------- */ | |
| function moveLeft(){if(inSingleView){restoreFromSingleView().then(()=>{selected=(selected-1+N)%N;updatePositions();});return;}selected=(selected-1+N)%N;updatePositions();} | |
| function moveRight(){if(inSingleView){restoreFromSingleView().then(()=>{selected=(selected+1)%N;updatePositions();});return;}selected=(selected+1)%N;updatePositions();} | |
| $(window).on('keydown',(ev)=>{const tag=(document.activeElement&&document.activeElement.tagName)||"";if(tag==="INPUT"||tag==="TEXTAREA")return;const k=ev.key;if(k==='a'||k==='A'||k==='ArrowLeft'){ev.preventDefault();moveLeft();}else if(k==='d'||k==='D'||k==='ArrowRight'){ev.preventDefault();moveRight();}else if(k==='Enter'){ev.preventDefault();if(!animating&&!inSingleView)handleEnterOnSelected();}else if(k==='r'||k==='R'){ev.preventDefault();if(inSingleView&&!animating)restoreFromSingleView();else if(currentMenu==='options'&&!animating)returnToParent();}}); | |
| $('#btnLeft').on('click',moveLeft);$('#btnRight').on('click',moveRight);$(window).on('resize',()=>{setTimeout(updatePositions,60);}); | |
| /* ---------- INIT ---------- */ | |
| function initMain(){menuData=mainGames.slice();buildMenu(menuData);selected=0;$('#menuTitle').text('Main Menu');setTimeout(()=>{animateEntrance(19000);},50);} | |
| $(function(){initMain();}); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment