Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created February 27, 2026 21:04
Show Gist options
  • Select an option

  • Save EncodeTheCode/90c4c7205e18d3c39c7c3c40443addc3 to your computer and use it in GitHub Desktop.

Select an option

Save EncodeTheCode/90c4c7205e18d3c39c7c3c40443addc3 to your computer and use it in GitHub Desktop.
<!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;
}
.game img{
width:100%; height:100%;
object-fit:cover;
display:block;
border-radius:inherit;
pointer-events:none;
}
.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; }
.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;}
.game-title{position:absolute;bottom:86px;left:50%;transform:translateX(-50%);color:#bfbfbf;font-size:18px;letter-spacing:1.6px;text-transform:uppercase;text-align:center;width:min(900px,94vw);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0.98;}
</style>
</head>
<body>
<div class="stage">
<div class="carousel" id="carousel"></div>
<div class="controls">
<div id="menuTitle">Main Menu</div>
<div style="display:flex;gap:10px;">
<button class="arrowBtn" id="btnLeft">A / ←</button>
<button class="arrowBtn" id="btnRight">D / →</button>
</div>
</div>
<div class="game-title" id="gameTitle"></div>
</div>
<script>
/* ================= MENU DATA ================= */
const mainGames = [
{ title: 'Crash Bandicoot', color: '#b2362f', image: 'images/crash.jpg' },
{ title: 'Spyro the Dragon', color: '#2f6fb2' },
{ title: 'Tekken 3', color: '#2fb280' },
{ title: 'Final Fantasy VII', color: '#b28b2f' },
{ title: 'Metal Gear Solid', color: '#8a2fb2' },
{ title: 'Options', color: '#4c6fb2' }
];
const optionsGames = [
{ title: 'Display Settings', color: '#3f7fb2' },
{ title: 'Audio Settings', color: '#5fb27f' },
{ title: 'Controls', color: '#b27f3f' },
{ title: 'Back', color: '#4c6fb2' }
];
/* ================= STATE ================= */
let menuData = mainGames.slice();
let currentMenu = 'main';
let selected = 0;
let nodes = [];
let animating = false;
const $carousel = $('#carousel');
/* ================= SVG FALLBACK ================= */
function escapeForSVG(s){
return String(s).replace(/&/g,'&amp;');
}
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-size='30'
fill='white'
dominant-baseline='middle'
text-anchor='middle'
font-family='Arial'>${safe}</text>
</svg>`;
return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
}
/* ================= BUILD MENU ================= */
function buildMenu(menuArray){
$carousel.empty();
nodes = [];
menuArray.forEach((g,i)=>{
const el = document.createElement('div');
el.className = 'game';
el.dataset.index = i;
const img = document.createElement('img');
img.alt = g.title.replace(/&amp;/g,'&');
// ===== IMAGE SUPPORT ADDED HERE =====
if(g.image){
img.src = g.image;
img.onerror = function(){
this.onerror = null;
this.src = makeSVGData(
g.title.replace(/&amp;/g,'&'),
g.color || '#444'
);
};
} else {
img.src = makeSVGData(
g.title.replace(/&amp;/g,'&'),
g.color || '#444'
);
}
// =====================================
el.appendChild(img);
$carousel.append(el);
nodes.push(el);
el.addEventListener('click', ()=>{
selected = i;
updatePositions();
});
});
updatePositions();
}
/* ================= POSITIONING ================= */
function updatePositions(){
const total = nodes.length;
const rX = 320;
const rY = 180;
nodes.forEach((node,i)=>{
let offset = i - selected;
if(offset > total/2) offset -= total;
if(offset < -total/2) offset += total;
const angle = (offset/total) * Math.PI * 2;
const x = Math.sin(angle) * rX;
const y = Math.cos(angle) * rY + 40;
node.style.transform =
`translate(-50%,-50%) translate(${x}px, ${y}px)`;
node.classList.toggle('front', offset === 0);
});
document.getElementById('gameTitle').textContent =
menuData[selected].title.replace(/&amp;/g,'&');
}
/* ================= NAVIGATION ================= */
function moveLeft(){
selected = (selected - 1 + menuData.length) % menuData.length;
updatePositions();
}
function moveRight(){
selected = (selected + 1) % menuData.length;
updatePositions();
}
function handleEnter(){
const title = menuData[selected].title.replace(/&amp;/g,'&');
if(title === "Options"){
menuData = optionsGames.slice();
currentMenu = 'options';
selected = 0;
buildMenu(menuData);
}
else if(title === "Back"){
menuData = mainGames.slice();
currentMenu = 'main';
selected = 0;
buildMenu(menuData);
}
}
$(window).on('keydown', e=>{
if(e.key === 'ArrowLeft' || e.key === 'a') moveLeft();
if(e.key === 'ArrowRight' || e.key === 'd') moveRight();
if(e.key === 'Enter') handleEnter();
});
$('#btnLeft').on('click', moveLeft);
$('#btnRight').on('click', moveRight);
/* ================= INIT ================= */
buildMenu(menuData);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment