LoFinity / frontend /index.html
eloigil6's picture
Add a top-right mute button that silences all audio (persisted)
8e4f3a3
Raw
History Blame Contribute Delete
13.5 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LoFinity — chill beats, freshly vended</title>
<meta
name="description"
content="A magical vending machine that dispenses endless lofi cassette tapes. Tell it a vibe; it brews you a beat."
/>
<!-- Open Graph / Discord / social preview (single-line per tag so simple
scrapers parse them). Absolute URLs point at the deployed Space;
the image is built by scripts/make_og.py. -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="LoFinity" />
<meta
property="og:title"
content="LoFinity — chill beats, freshly vended"
/>
<meta
property="og:description"
content="A magical vending machine that dispenses endless lofi cassette tapes. Tell it a vibe; it brews you a beat."
/>
<meta
property="og:url"
content="https://build-small-hackathon-lofinity.hf.space/"
/>
<meta
property="og:image"
content="https://build-small-hackathon-lofinity.hf.space/static/og.png"
/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="LoFinity title screen — chill beats, freshly vended"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="LoFinity — chill beats, freshly vended"
/>
<meta
name="twitter:description"
content="A magical vending machine that dispenses endless lofi cassette tapes."
/>
<meta
name="twitter:image"
content="https://build-small-hackathon-lofinity.hf.space/static/og.png"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@600;800&family=DotGothic16&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/static/style.css" />
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js"
}
}
</script>
</head>
<body>
<div id="title-overlay">
<h1 id="title">LoFinity</h1>
<div class="subtitle-wrap">
<p id="subtitle">♪ chill beats, freshly vended</p>
</div>
<button id="start-btn" type="button">▶ click to start</button>
</div>
<button
id="mute-btn"
class="mute-btn"
type="button"
aria-label="mute all sound"
aria-pressed="false"
title="mute"
>
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M4 9 L7.5 9 L13 5 L13 19 L7.5 15 L4 15 Z" fill="currentColor" stroke="none" />
<g class="mute-waves">
<path d="M16 9.5a4 4 0 0 1 0 5" />
<path d="M18.6 7a8 8 0 0 1 0 10" />
</g>
<line class="mute-slash" x1="3.5" y1="3.5" x2="20.5" y2="20.5" />
</svg>
</button>
<div id="vending-label" class="hover-label"><span>♪ vend a vibe</span></div>
<div id="collection-label" class="hover-label">
<span>♪ cassette collection</span>
</div>
<div id="lamp-label" class="hover-label"><span>☾ turn on night</span></div>
<div id="gameboy-label" class="hover-label"><span>▸ play</span></div>
<div id="machine-modal" class="hidden">
<button id="modal-close" aria-label="close">×</button>
<div id="led-screen">
<p class="led-title">PICK YOUR VIBE<span class="led-cursor">_</span></p>
<p class="led-sub">TELL THE MACHINE WHAT TO BREW</p>
<textarea
id="prompt-input"
rows="3"
maxlength="200"
placeholder="rainy night in kyoto, soft piano, vinyl crackle..."
></textarea>
<div id="length-row">
<span class="len-label">LENGTH</span>
<input
id="length-slider"
type="range"
min="0"
max="2"
step="1"
value="0"
aria-label="tape length"
/>
<span id="length-value">0:30</span>
</div>
<div id="generating" class="hidden">
<div class="grow-loader" aria-hidden="true">
<span class="sprout">
<i class="stem"></i><i class="leaf l"></i><i class="leaf r"></i><i class="bloom"></i>
</span>
<span class="sprout">
<i class="stem"></i><i class="leaf l"></i><i class="leaf r"></i><i class="bloom"></i>
</span>
<span class="sprout">
<i class="stem"></i><i class="leaf l"></i><i class="leaf r"></i><i class="bloom"></i>
</span>
<span class="sprout">
<i class="stem"></i><i class="leaf l"></i><i class="leaf r"></i><i class="bloom"></i>
</span>
</div>
<div id="brew-bar" aria-hidden="true"><span id="brew-bar-fill"></span></div>
<p class="led-status">
BREWING YOUR BEAT<span class="dots">...</span>
</p>
<p class="led-sub">REAL TAPES TAKE A MINUTE OR TWO</p>
</div>
<p id="error-msg" class="hidden"></p>
</div>
<div id="controls-row">
<div id="coin-slot-area">
<div id="coin-slot"></div>
<svg id="wado-coin" viewBox="0 0 100 100" aria-hidden="true">
<defs>
<radialGradient id="bronze" cx="35%" cy="30%" r="80%">
<stop offset="0%" stop-color="#dcb670" />
<stop offset="65%" stop-color="#b08540" />
<stop offset="100%" stop-color="#8a6630" />
</radialGradient>
</defs>
<circle
cx="50"
cy="50"
r="47"
fill="url(#bronze)"
stroke="#7a5a28"
stroke-width="4"
/>
<circle
cx="50"
cy="50"
r="38"
fill="none"
stroke="#8a6a35"
stroke-width="1.5"
opacity="0.6"
/>
<rect
x="41"
y="41"
width="18"
height="18"
fill="#3b2c16"
stroke="#7a5a28"
stroke-width="3"
/>
<text
x="50"
y="26"
text-anchor="middle"
dominant-baseline="central"
class="wado-kanji"
>
</text>
<text
x="75"
y="51"
text-anchor="middle"
dominant-baseline="central"
class="wado-kanji"
>
</text>
<text
x="50"
y="76"
text-anchor="middle"
dominant-baseline="central"
class="wado-kanji"
>
</text>
<text
x="25"
y="51"
text-anchor="middle"
dominant-baseline="central"
class="wado-kanji"
>
</text>
</svg>
</div>
<button id="coin-button">INSERT COIN</button>
</div>
<div id="cassette-stage" class="hidden">
<div id="cassette">
<div class="cassette-label"><span id="cassette-title"></span></div>
<div class="reels">
<span class="reel"></span>
<span class="tape-window"></span>
<span class="reel"></span>
</div>
</div>
<div id="player">
<button id="play-btn" aria-label="play/pause"></button>
<div id="progress"><div id="progress-fill"></div></div>
<span id="time">0:00</span>
</div>
<button id="again-btn">MAKE ANOTHER</button>
</div>
<button
id="play-while-waiting"
class="hidden"
aria-label="play the garden game while you wait"
>
<svg
viewBox="0 0 24 24"
width="22"
height="22"
fill="none"
stroke="currentColor"
stroke-width="1.7"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<rect x="5" y="2.5" width="14" height="19" rx="2.6" />
<rect x="8" y="5" width="8" height="6.5" rx="1" />
<path d="M9 16.6h2.4M10.2 15.4v2.4" />
<circle cx="15" cy="15.6" r="0.95" fill="currentColor" stroke="none" />
<circle cx="16.6" cy="17.1" r="0.95" fill="currentColor" stroke="none" />
</svg>
<span>play a relaxed game while waiting</span>
</button>
</div>
<div id="collection-panel" class="hidden">
<p id="collection-heading"><span>♪ cassette collection</span></p>
<button id="collection-close" aria-label="close collection">×</button>
<p id="collection-status" class="hidden"></p>
<div id="carousel-row">
<button class="caro-btn" id="caro-prev" aria-label="scroll left">
</button>
<div id="carousel"></div>
<button class="caro-btn" id="caro-next" aria-label="scroll right">
</button>
</div>
</div>
<div id="tape-deck" class="hidden">
<button id="deck-play" aria-label="play/pause"></button>
<div id="deck-info">
<span id="deck-title">pick a tape</span>
<div id="deck-progress"><div id="deck-fill"></div></div>
</div>
<span id="deck-time">0:00</span>
<div id="deck-cassette" aria-hidden="true">
<span class="mini-reel"></span>
<span class="mini-reel"></span>
</div>
<button
id="deck-loop"
class="on"
aria-label="loop this tape"
aria-pressed="true"
title="looping this tape — click to play the whole shelf"
>
<svg
viewBox="0 0 24 24"
width="20"
height="20"
fill="none"
stroke="currentColor"
stroke-width="2.4"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<polyline points="17 1 21 5 17 9" />
<path d="M3 11V9a4 4 0 0 1 4-4h14" />
<polyline points="7 23 3 19 7 15" />
<path d="M21 13v2a4 4 0 0 1-4 4H3" />
</svg>
</button>
<a
id="deck-download"
class="disabled"
aria-label="download tape"
title="download tape"
>
<svg
viewBox="0 0 24 24"
width="20"
height="20"
fill="none"
stroke="currentColor"
stroke-width="2.4"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="M12 3v12" />
<path d="m6 11 6 6 6-6" />
<path d="M5 21h14" />
</svg>
</a>
</div>
<div id="gameboy-modal" class="hidden">
<button id="gameboy-close" aria-label="close">×</button>
<div class="gb-shell">
<div class="gb-screen-frame">
<div class="gb-power"><i></i>POWER</div>
<div id="gameboy-screen" class="gb-screen">
<canvas id="gb-canvas" width="160" height="144"></canvas>
</div>
</div>
<div id="gb-caption" class="gb-caption"></div>
<div class="gb-logo">NO DOPAMINE<span></span></div>
<div class="gb-controls">
<div class="gb-dpad">
<span class="gb-pad-v"></span><span class="gb-pad-h"></span>
<button
class="gb-pad-hit gb-up"
data-dir="up"
aria-label="up"
></button>
<button
class="gb-pad-hit gb-down"
data-dir="down"
aria-label="down"
></button>
<button
class="gb-pad-hit gb-left"
data-dir="left"
aria-label="left"
></button>
<button
class="gb-pad-hit gb-right"
data-dir="right"
aria-label="right"
></button>
</div>
<div class="gb-ab">
<button class="gb-btn" data-action="b">B</button>
<button class="gb-btn" data-action="a">A</button>
</div>
</div>
<div class="gb-startsel">
<button
class="gb-ss"
data-action="select"
aria-label="select (change seed)"
></button>
<button
class="gb-ss"
data-action="start"
aria-label="start (dig up)"
></button>
</div>
<div class="gb-speaker" aria-hidden="true">
<i></i><i></i><i></i><i></i><i></i><i></i>
</div>
</div>
</div>
<div id="now-playing" class="hidden">
<button id="np-toggle" aria-label="play/pause">❚❚</button>
<span id="np-title"></span>
</div>
<audio id="tape-audio"></audio>
<canvas id="scene"></canvas>
<script type="module" src="/static/main.js"></script>
</body>
</html>