996bd6b3f9
Focus mode is more likely to cause unexpected audio silencing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1296 lines
40 KiB
HTML
1296 lines
40 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>Wario Synth - Game Boy Midi Synthesis Engine</title>
|
|
|
|
<!-- OpenGraph / Social Media Preview -->
|
|
<meta property="og:title" content="Wario Synth 16-Bit Midi">
|
|
<meta property="og:description" content="Turn any song into a Game Boy version">
|
|
<meta property="og:image" content="/wariosynthlogo.png">
|
|
<meta property="og:image:width" content="1180">
|
|
<meta property="og:image:height" content="912">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:url" content="https://motif-self.vercel.app">
|
|
|
|
<!-- Twitter Card -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="Wario Synth 16-Bit Midi">
|
|
<meta name="twitter:description" content="Turn any song into a Game Boy version">
|
|
<meta name="twitter:image" content="/wariosynthlogo.png">
|
|
|
|
<!-- General Meta -->
|
|
<meta name="description" content="WARIO SYNTH - Transform any song into retro Gameboy-style game console music. Search, preview MIDI, and generate chiptune versions.">
|
|
|
|
<style>
|
|
/* Local Pixel Font */
|
|
@font-face {
|
|
font-family: 'Press Start 2P';
|
|
src: url('/fonts/PressStart2P.woff2') format('woff2');
|
|
font-weight: 400;
|
|
font-style: normal;
|
|
font-display: swap;
|
|
}
|
|
|
|
/* Design System: Game Boy DMG - Authentic light LCD look */
|
|
:root {
|
|
/* Game Boy palette - light mode (authentic LCD) */
|
|
--gb-lightest: #E0E8C8; /* Screen background */
|
|
--gb-light: #A8B89A; /* Surface/cards */
|
|
--gb-medium: #6B7B5A; /* Dim text, borders */
|
|
--gb-dark: #3A4A2A; /* Text */
|
|
--gb-darkest: #1D2A1D; /* Darkest accents */
|
|
|
|
/* Console shell colors */
|
|
--gb-shell: #C4CFA1;
|
|
--gb-shell-dark: #8B956D;
|
|
|
|
/* Semantic mapping - LIGHT background, DARK text */
|
|
--color-bg: #C8D8B0;
|
|
--color-surface: #A8B89A;
|
|
--color-surface-elevated: #B8C8A8;
|
|
--color-border: var(--gb-dark);
|
|
--color-text: var(--gb-darkest);
|
|
--color-text-dim: var(--gb-medium);
|
|
--color-accent-primary: var(--gb-dark);
|
|
--color-accent-secondary: var(--gb-medium);
|
|
|
|
--spacing-unit: 8px;
|
|
--radius: 0px; /* Pixel-perfect, no rounding */
|
|
--pixel-border: 4px;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
image-rendering: pixelated;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Press Start 2P', monospace;
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
margin: 0;
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
line-height: 2;
|
|
font-size: 10px;
|
|
-webkit-font-smoothing: none;
|
|
-moz-osx-font-smoothing: unset;
|
|
}
|
|
|
|
/* LCD Screen Effect - subtle on light bg */
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
inset: 0;
|
|
background: repeating-linear-gradient(
|
|
0deg,
|
|
rgba(0, 0, 0, 0.03) 0px,
|
|
rgba(0, 0, 0, 0.03) 2px,
|
|
transparent 2px,
|
|
transparent 4px
|
|
);
|
|
pointer-events: none;
|
|
z-index: 9999;
|
|
}
|
|
|
|
.container {
|
|
max-width: 960px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.hero {
|
|
text-align: center;
|
|
margin: 0 0 calc(var(--spacing-unit) * 4) 0;
|
|
position: relative;
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
background: var(--color-surface);
|
|
}
|
|
.hero h1 {
|
|
color: var(--gb-darkest);
|
|
margin: 0;
|
|
font-size: 20px;
|
|
font-weight: normal;
|
|
letter-spacing: 0.05em;
|
|
text-transform: uppercase;
|
|
}
|
|
.hero-top {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: calc(var(--spacing-unit) * 2);
|
|
flex-wrap: wrap;
|
|
}
|
|
.hero-top h1 {
|
|
margin: 0;
|
|
}
|
|
.version-tag {
|
|
font-size: 10px;
|
|
color: var(--gb-medium);
|
|
font-weight: normal;
|
|
}
|
|
.hero .tagline {
|
|
margin: calc(var(--spacing-unit) * 2) 0 0 0;
|
|
color: var(--gb-dark);
|
|
font-size: 8px;
|
|
}
|
|
.version-history {
|
|
margin: calc(var(--spacing-unit) * 2) 0 calc(var(--spacing-unit) * 3) 0;
|
|
text-align: center;
|
|
}
|
|
.version-history details {
|
|
display: inline-block;
|
|
max-width: 600px;
|
|
}
|
|
.version-history summary {
|
|
cursor: pointer;
|
|
font-size: 8px;
|
|
color: var(--gb-medium);
|
|
list-style: none;
|
|
padding: 8px 16px;
|
|
border: 2px solid var(--gb-medium);
|
|
background: var(--color-surface);
|
|
transition: all 0.15s ease;
|
|
}
|
|
.version-history summary::-webkit-details-marker { display: none; }
|
|
.version-history summary:hover {
|
|
color: var(--gb-darkest);
|
|
border-color: var(--gb-dark);
|
|
background: var(--gb-lightest);
|
|
}
|
|
.version-history details[open] summary {
|
|
border-bottom: none;
|
|
background: var(--gb-dark);
|
|
color: var(--gb-lightest);
|
|
border-color: var(--gb-dark);
|
|
}
|
|
.version-content {
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
background: var(--gb-dark);
|
|
border: 2px solid var(--gb-dark);
|
|
border-top: none;
|
|
text-align: left;
|
|
}
|
|
.version-item {
|
|
font-size: 8px;
|
|
color: var(--gb-light);
|
|
line-height: 2;
|
|
margin-bottom: 8px;
|
|
}
|
|
.version-item:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
.version-item strong {
|
|
color: var(--gb-lightest);
|
|
}
|
|
.version-divider {
|
|
height: 2px;
|
|
background: var(--gb-medium);
|
|
margin: calc(var(--spacing-unit) * 2) 0;
|
|
opacity: 0.5;
|
|
}
|
|
.faq-link {
|
|
background: var(--gb-lightest);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
color: var(--gb-darkest);
|
|
padding: 8px 12px;
|
|
border-radius: 0;
|
|
box-shadow:
|
|
inset -3px -3px 0 var(--gb-medium),
|
|
inset 3px 3px 0 #fff;
|
|
font-size: 8px;
|
|
}
|
|
.faq-link:hover:not(:disabled) {
|
|
background: #fff;
|
|
color: var(--gb-darkest);
|
|
}
|
|
|
|
/* FAQ modal - Game Boy style */
|
|
.modal-backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0,0,0,0.5);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
z-index: 1000;
|
|
}
|
|
.modal-backdrop.open { display: flex; }
|
|
.modal {
|
|
width: min(720px, 100%);
|
|
max-height: calc(100vh - 40px);
|
|
max-height: calc(100dvh - 40px);
|
|
background: var(--color-surface);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.modal header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 14px 16px;
|
|
border-bottom: 2px solid var(--gb-dark);
|
|
background: var(--gb-dark);
|
|
flex-shrink: 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
.modal header h3 {
|
|
margin: 0;
|
|
font-size: 10px;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--gb-lightest);
|
|
}
|
|
.modal header .faq-header-links {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.modal header .faq-header-links a {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 6px 10px;
|
|
border: 2px solid var(--gb-light);
|
|
background: var(--gb-darkest);
|
|
color: var(--gb-lightest);
|
|
text-decoration: none;
|
|
font-size: 8px;
|
|
line-height: 1;
|
|
}
|
|
.modal header .faq-header-links a:hover {
|
|
background: var(--gb-light);
|
|
color: var(--gb-darkest);
|
|
}
|
|
.modal header .faq-header-links .dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
background: var(--gb-lightest);
|
|
flex: 0 0 auto;
|
|
}
|
|
.modal header .faq-header-links .label {
|
|
display: inline-flex;
|
|
align-items: baseline;
|
|
gap: 6px;
|
|
white-space: nowrap;
|
|
}
|
|
.modal header .faq-header-links .handle {
|
|
color: var(--gb-lightest);
|
|
}
|
|
@media (max-width: 480px) {
|
|
.modal header {
|
|
gap: 10px;
|
|
padding: 12px 12px;
|
|
}
|
|
.modal header .faq-header-links a {
|
|
padding: 6px 9px;
|
|
font-size: 8px;
|
|
}
|
|
}
|
|
.modal header .spacer { margin-left: auto; }
|
|
.modal header button {
|
|
padding: 8px 12px;
|
|
background: var(--gb-light);
|
|
border: 2px solid var(--gb-lightest);
|
|
color: var(--gb-darkest);
|
|
font-size: 8px;
|
|
}
|
|
.modal header button:hover {
|
|
background: var(--gb-lightest);
|
|
}
|
|
.modal main {
|
|
padding: 16px;
|
|
background: var(--gb-lightest);
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
.faq-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 10px;
|
|
}
|
|
.faq-item {
|
|
border: 2px solid var(--gb-dark);
|
|
padding: 10px;
|
|
background: var(--color-surface);
|
|
}
|
|
.faq-item h4 {
|
|
margin: 0 0 6px 0;
|
|
font-size: 8px;
|
|
color: var(--gb-darkest);
|
|
}
|
|
.faq-item p {
|
|
margin: 0;
|
|
color: var(--gb-dark);
|
|
font-size: 8px;
|
|
line-height: 1.8;
|
|
}
|
|
.faq-item code {
|
|
font-size: 8px;
|
|
color: var(--gb-darkest);
|
|
}
|
|
.controls {
|
|
margin: calc(var(--spacing-unit) * 3) 0 calc(var(--spacing-unit) * 2) 0;
|
|
display: flex;
|
|
gap: calc(var(--spacing-unit) * 1.5);
|
|
align-items: stretch;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
.controls.locked {
|
|
opacity: 0.35;
|
|
}
|
|
|
|
/* Chunky 8-bit buttons */
|
|
button {
|
|
background: var(--color-surface);
|
|
color: var(--color-text);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
padding: 12px 16px;
|
|
border-radius: 0;
|
|
cursor: pointer;
|
|
font-family: 'Press Start 2P', monospace;
|
|
font-size: 8px;
|
|
text-transform: uppercase;
|
|
box-shadow:
|
|
inset -3px -3px 0 var(--gb-medium),
|
|
inset 3px 3px 0 var(--gb-lightest);
|
|
transition: none;
|
|
}
|
|
|
|
button:hover:not(:disabled) {
|
|
background: var(--gb-lightest);
|
|
color: var(--gb-darkest);
|
|
}
|
|
|
|
button:active:not(:disabled) {
|
|
box-shadow:
|
|
inset 3px 3px 0 var(--gb-medium);
|
|
}
|
|
|
|
button:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
button.primary {
|
|
background: var(--gb-dark);
|
|
color: var(--gb-lightest);
|
|
border-color: var(--gb-darkest);
|
|
box-shadow:
|
|
inset -3px -3px 0 var(--gb-darkest),
|
|
inset 3px 3px 0 var(--gb-medium);
|
|
}
|
|
|
|
button.primary:hover:not(:disabled) {
|
|
background: var(--gb-darkest);
|
|
color: var(--gb-lightest);
|
|
}
|
|
|
|
/* Search button */
|
|
#searchBtn {
|
|
padding: 14px 20px;
|
|
font-size: 10px;
|
|
}
|
|
|
|
/* Status bar - like a Game Boy dialog box */
|
|
#status {
|
|
margin: calc(var(--spacing-unit) * 3) 0;
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
background: var(--gb-lightest);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
color: var(--gb-darkest);
|
|
font-size: 8px;
|
|
min-height: 24px;
|
|
}
|
|
|
|
/* Text input - Game Boy style */
|
|
input[type="text"] {
|
|
flex: 1;
|
|
min-width: 0;
|
|
background: var(--gb-lightest);
|
|
color: var(--gb-darkest);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
padding: 12px 14px;
|
|
border-radius: 0;
|
|
font-family: 'Press Start 2P', monospace;
|
|
font-size: 10px;
|
|
box-shadow: inset 3px 3px 0 var(--gb-medium);
|
|
}
|
|
|
|
input[type="text"]:focus {
|
|
outline: none;
|
|
border-color: var(--gb-darkest);
|
|
background: #fff;
|
|
}
|
|
|
|
input[type="text"]::placeholder {
|
|
color: var(--gb-medium);
|
|
}
|
|
|
|
.results-section {
|
|
margin: 30px 0;
|
|
display: none;
|
|
}
|
|
.results-section.visible {
|
|
display: block;
|
|
}
|
|
.results-section.collapsed {
|
|
opacity: 0.55;
|
|
transition: opacity 160ms ease;
|
|
}
|
|
.results-section.collapsed:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.chooser-helper {
|
|
margin-top: calc(var(--spacing-unit) * 2);
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
background: var(--color-surface);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
}
|
|
.chooser-helper h4 {
|
|
margin: 0 0 8px 0;
|
|
font-size: 8px;
|
|
text-transform: uppercase;
|
|
color: var(--gb-darkest);
|
|
}
|
|
.chooser-helper p {
|
|
margin: 0;
|
|
font-size: 8px;
|
|
color: var(--gb-dark);
|
|
line-height: 2;
|
|
}
|
|
|
|
/* Results table - Game Boy menu style */
|
|
.results-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: calc(var(--spacing-unit) * 2) 0;
|
|
background: var(--color-surface);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
}
|
|
|
|
.results-table th,
|
|
.results-table td {
|
|
padding: 12px 16px;
|
|
text-align: left;
|
|
border-bottom: 2px solid var(--gb-medium);
|
|
font-size: 8px;
|
|
color: var(--gb-darkest);
|
|
}
|
|
|
|
.results-table th {
|
|
background: var(--gb-dark);
|
|
color: var(--gb-lightest);
|
|
font-weight: normal;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.results-table .source-col {
|
|
width: 100px;
|
|
text-align: center;
|
|
}
|
|
|
|
.results-table .duration-col {
|
|
width: 80px;
|
|
text-align: right;
|
|
}
|
|
.results-table .action-col {
|
|
width: 120px;
|
|
text-align: right;
|
|
}
|
|
|
|
.results-table tbody tr {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.results-table tbody tr:hover {
|
|
background: var(--gb-lightest);
|
|
}
|
|
|
|
.results-table tbody tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.results-table tr.selected {
|
|
background: var(--gb-dark);
|
|
color: var(--gb-lightest);
|
|
}
|
|
|
|
.results-table tr.selected td {
|
|
color: var(--gb-lightest);
|
|
}
|
|
|
|
.results-table tr.selected td:first-child {
|
|
padding-left: 12px;
|
|
}
|
|
|
|
/* Selection arrow indicator */
|
|
.results-table tr.selected td:first-child::before {
|
|
content: '▶ ';
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.results-table th,
|
|
.results-table td {
|
|
padding: 10px 12px;
|
|
font-size: 7px;
|
|
}
|
|
|
|
.results-table .source-col {
|
|
display: none;
|
|
}
|
|
|
|
.results-table .duration-col {
|
|
width: 60px;
|
|
}
|
|
|
|
.results-table .action-col {
|
|
width: 90px;
|
|
}
|
|
}
|
|
|
|
.results-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: calc(var(--spacing-unit) * 2);
|
|
flex-wrap: wrap;
|
|
margin: 0 0 calc(var(--spacing-unit) * 2) 0;
|
|
}
|
|
.results-header h3 { margin: 0; }
|
|
|
|
.confidence-bar {
|
|
width: 60px;
|
|
height: 4px;
|
|
background: var(--gb-darkest);
|
|
overflow: hidden;
|
|
}
|
|
.confidence-fill {
|
|
height: 100%;
|
|
background: var(--gb-lightest);
|
|
}
|
|
|
|
/* Selected source card - Game Boy cartridge style */
|
|
.selected-card {
|
|
margin-top: calc(var(--spacing-unit) * 3);
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
background: var(--gb-dark);
|
|
border: var(--pixel-border) solid var(--gb-light);
|
|
}
|
|
.selected-title {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 2;
|
|
font-weight: normal;
|
|
color: var(--gb-lightest);
|
|
}
|
|
.selected-meta {
|
|
margin: 12px 0 0 0;
|
|
font-size: 8px;
|
|
color: var(--gb-light);
|
|
}
|
|
.selected-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
margin-top: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.selected-actions .primary {
|
|
flex: 1;
|
|
min-width: 160px;
|
|
padding: 14px 16px;
|
|
}
|
|
.selected-actions button:not(.primary) {
|
|
flex: 1;
|
|
min-width: 140px;
|
|
}
|
|
.selected-support {
|
|
display: flex;
|
|
gap: 16px;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
margin-top: 16px;
|
|
}
|
|
.selected-support .linklike {
|
|
background: transparent;
|
|
border: none;
|
|
padding: 0;
|
|
box-shadow: none;
|
|
color: var(--gb-lightest);
|
|
cursor: pointer;
|
|
font-size: 8px;
|
|
text-decoration: underline;
|
|
}
|
|
.selected-support .linklike:hover {
|
|
color: var(--gb-light);
|
|
}
|
|
.preview-inline {
|
|
display: inline-flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
.preview-state {
|
|
font-size: 8px;
|
|
color: var(--gb-light);
|
|
display: none;
|
|
}
|
|
|
|
/* Copy link button */
|
|
#copyLinkBtn {
|
|
padding: 12px 16px;
|
|
}
|
|
#copyLinkBtn:hover:not(:disabled) {
|
|
background: var(--gb-light);
|
|
color: var(--gb-darkest);
|
|
}
|
|
.engine-line {
|
|
margin-top: 14px;
|
|
color: var(--gb-light);
|
|
font-size: 8px;
|
|
}
|
|
details.details-toggle {
|
|
margin-top: 12px;
|
|
}
|
|
details.details-toggle summary {
|
|
cursor: pointer;
|
|
color: var(--gb-light);
|
|
font-size: 8px;
|
|
list-style: none;
|
|
}
|
|
details.details-toggle summary::-webkit-details-marker { display: none; }
|
|
.selected-details {
|
|
margin-top: 8px;
|
|
font-size: 8px;
|
|
color: var(--gb-light);
|
|
}
|
|
|
|
.footer {
|
|
margin-top: calc(var(--spacing-unit) * 6);
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: calc(var(--spacing-unit) * 1.5);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.footer-mascot {
|
|
margin-top: calc(var(--spacing-unit) * 4);
|
|
text-align: center;
|
|
}
|
|
|
|
.wario-sprite {
|
|
width: 192px;
|
|
height: auto;
|
|
mix-blend-mode: multiply;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.player-section {
|
|
margin: 30px 0;
|
|
display: none;
|
|
}
|
|
.player-section.visible {
|
|
display: block;
|
|
}
|
|
|
|
.player-controls {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 20px;
|
|
margin: 20px 0;
|
|
}
|
|
.player-box {
|
|
background: var(--color-surface);
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
border-radius: 0;
|
|
}
|
|
|
|
.player-box:hover {
|
|
background: var(--gb-lightest);
|
|
}
|
|
|
|
.player-box h4 {
|
|
margin: 0 0 calc(var(--spacing-unit)) 0;
|
|
color: var(--gb-darkest);
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.player-box p {
|
|
font-size: 13px;
|
|
color: var(--gb-dark);
|
|
margin: 0 0 calc(var(--spacing-unit) * 2) 0;
|
|
}
|
|
|
|
.player-box button {
|
|
width: 100%;
|
|
margin: calc(var(--spacing-unit)) 0 0 0;
|
|
}
|
|
|
|
.player-box button:first-of-type {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.player-info {
|
|
background: var(--color-surface);
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
margin: calc(var(--spacing-unit) * 3) 0;
|
|
border-left: 3px solid var(--color-accent-primary);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.player-info h4 {
|
|
margin: 0 0 calc(var(--spacing-unit)) 0;
|
|
color: var(--color-text);
|
|
font-size: 16px;
|
|
}
|
|
|
|
.player-info p {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
color: var(--color-text-dim);
|
|
}
|
|
|
|
.section {
|
|
margin-top: calc(var(--spacing-unit) * 5);
|
|
}
|
|
|
|
.section-header {
|
|
margin: 0 0 calc(var(--spacing-unit) * 2) 0;
|
|
}
|
|
|
|
.section-title {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.section-subtitle {
|
|
margin: calc(var(--spacing-unit) / 2) 0 0 0;
|
|
font-size: 13px;
|
|
color: var(--color-text-dim);
|
|
}
|
|
|
|
.divider {
|
|
height: 1px;
|
|
margin: 0 0 calc(var(--spacing-unit) * 3) 0;
|
|
background: linear-gradient(90deg, transparent, var(--color-border), transparent);
|
|
}
|
|
|
|
.preview-controls {
|
|
display: flex;
|
|
gap: calc(var(--spacing-unit) * 1.5);
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
background: var(--color-surface);
|
|
border-radius: var(--radius);
|
|
border: 1px solid var(--color-border);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.preview-controls button.preview-play {
|
|
flex: 1;
|
|
min-width: 160px;
|
|
}
|
|
|
|
.preview-controls button.next-result {
|
|
margin-left: 0;
|
|
}
|
|
|
|
.preview-controls .preview-actions {
|
|
margin-left: auto;
|
|
display: inline-flex;
|
|
gap: calc(var(--spacing-unit) * 1.25);
|
|
align-items: center;
|
|
}
|
|
|
|
.preview-controls .volume-control {
|
|
flex: 1;
|
|
min-width: 200px;
|
|
margin-top: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.preview-controls {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.preview-controls .preview-actions {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
.motif-stage {
|
|
background: var(--color-surface-elevated);
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
border-radius: var(--radius);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
}
|
|
.motif-grid {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
.motif-header {
|
|
margin-bottom: 6px;
|
|
}
|
|
.motif-controls {
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
.motif-controls button {
|
|
flex-shrink: 0;
|
|
}
|
|
.motif-controls button.primary {
|
|
flex: 1;
|
|
min-width: 180px;
|
|
padding: 12px 16px;
|
|
font-weight: 700;
|
|
}
|
|
.motif-controls button.primary:hover {
|
|
background: var(--gb-darkest);
|
|
}
|
|
.motif-controls .volume-control {
|
|
flex: 1;
|
|
min-width: 200px;
|
|
margin-top: 0;
|
|
}
|
|
.motif-controls .volume-control label {
|
|
font-size: 0.85em;
|
|
min-width: 55px;
|
|
}
|
|
|
|
/* iOS audio unlock (Motif) */
|
|
.ios-audio-banner {
|
|
margin-top: calc(var(--spacing-unit) * 2);
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
background: var(--gb-lightest);
|
|
display: none; /* toggled by JS */
|
|
}
|
|
.ios-audio-banner .row {
|
|
display: flex;
|
|
gap: calc(var(--spacing-unit) * 1.5);
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
.ios-audio-banner .copy {
|
|
font-size: 8px;
|
|
color: var(--gb-dark);
|
|
line-height: 2;
|
|
flex: 1;
|
|
min-width: 220px;
|
|
}
|
|
.ios-audio-banner .copy strong {
|
|
color: var(--gb-darkest);
|
|
}
|
|
.ios-audio-banner .state {
|
|
margin-top: calc(var(--spacing-unit) * 1);
|
|
font-size: 8px;
|
|
color: var(--gb-dark);
|
|
display: none;
|
|
}
|
|
.motif-kicker {
|
|
font-size: 8px;
|
|
color: var(--gb-dark);
|
|
margin: 0 0 10px 0;
|
|
max-width: 70ch;
|
|
}
|
|
.try-next {
|
|
margin-top: 14px;
|
|
width: 100%;
|
|
}
|
|
.try-next button {
|
|
width: 100%;
|
|
}
|
|
|
|
.volume-control {
|
|
margin-top: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
position: relative;
|
|
}
|
|
.volume-control label {
|
|
font-size: 8px;
|
|
color: var(--gb-dark);
|
|
}
|
|
.volume-control input[type="range"] {
|
|
flex: 1;
|
|
margin: 0;
|
|
}
|
|
|
|
.motif-progress-container {
|
|
margin-top: 8px;
|
|
padding-top: 16px;
|
|
border-top: 2px solid var(--gb-light);
|
|
}
|
|
.progress-time {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 8px;
|
|
font-size: 8px;
|
|
color: var(--gb-light);
|
|
}
|
|
.progress-bar-wrapper {
|
|
position: relative;
|
|
height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: calc(var(--spacing-unit) * 2) 0;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 8px;
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
background: transparent;
|
|
border-radius: 0;
|
|
outline: none;
|
|
cursor: pointer;
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
.progress-bar::-webkit-slider-runnable-track {
|
|
height: 8px;
|
|
background: var(--gb-darkest);
|
|
border-radius: 0;
|
|
}
|
|
|
|
.progress-bar::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 0;
|
|
background: var(--gb-lightest);
|
|
border: 2px solid var(--gb-light);
|
|
cursor: pointer;
|
|
margin-top: -4px;
|
|
}
|
|
|
|
.progress-bar:active::-webkit-slider-thumb {
|
|
background: #fff;
|
|
}
|
|
|
|
.progress-bar::-moz-range-track {
|
|
height: 8px;
|
|
background: var(--gb-darkest);
|
|
border-radius: 0;
|
|
}
|
|
|
|
.progress-bar::-moz-range-thumb {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 0;
|
|
background: var(--gb-lightest);
|
|
border: 2px solid var(--gb-light);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.progress-fill {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
height: 8px;
|
|
background: var(--gb-light);
|
|
border-radius: 0;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
width: 0%;
|
|
transition: width 0.1s linear;
|
|
}
|
|
|
|
/* Use on your website */
|
|
.embed-card {
|
|
background: var(--color-surface);
|
|
border: var(--pixel-border) solid var(--gb-dark);
|
|
padding: calc(var(--spacing-unit) * 3);
|
|
}
|
|
.embed-row {
|
|
display: flex;
|
|
gap: calc(var(--spacing-unit) * 1.5);
|
|
align-items: flex-start;
|
|
flex-wrap: wrap;
|
|
}
|
|
.embed-row button {
|
|
flex-shrink: 0;
|
|
}
|
|
.codeblock {
|
|
flex: 1;
|
|
min-width: 260px;
|
|
margin: 0;
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
background: var(--gb-lightest);
|
|
border: 2px solid var(--gb-medium);
|
|
overflow: auto;
|
|
}
|
|
.codeblock code {
|
|
font-size: 8px;
|
|
color: var(--gb-darkest);
|
|
white-space: pre;
|
|
}
|
|
.embed-hint {
|
|
margin-top: calc(var(--spacing-unit) * 1.5);
|
|
color: var(--gb-dark);
|
|
font-size: 8px;
|
|
}
|
|
.embed-hint strong {
|
|
color: var(--gb-darkest);
|
|
}
|
|
.copy-toast {
|
|
margin-left: auto;
|
|
font-size: 8px;
|
|
color: var(--gb-darkest);
|
|
display: none;
|
|
}
|
|
|
|
.engine-selector {
|
|
margin-top: 10px;
|
|
font-size: 8px;
|
|
}
|
|
.engine-selector label {
|
|
color: var(--gb-dark);
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
}
|
|
.engine-selector select {
|
|
background: var(--gb-lightest);
|
|
color: var(--gb-darkest);
|
|
border: 2px solid var(--gb-dark);
|
|
padding: 8px 12px;
|
|
font-family: 'Press Start 2P', monospace;
|
|
font-size: 8px;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Mobile: increase font sizes for readability */
|
|
@media (max-width: 640px) {
|
|
body {
|
|
font-size: 11px;
|
|
line-height: 2.2;
|
|
}
|
|
.hero h1 {
|
|
font-size: 18px;
|
|
}
|
|
.hero .tagline {
|
|
font-size: 9px;
|
|
}
|
|
button {
|
|
font-size: 10px;
|
|
padding: 14px 18px;
|
|
}
|
|
#searchBtn {
|
|
font-size: 11px;
|
|
}
|
|
input[type="text"] {
|
|
font-size: 11px;
|
|
}
|
|
#status {
|
|
font-size: 9px;
|
|
}
|
|
.chooser-helper h4,
|
|
.chooser-helper p {
|
|
font-size: 9px;
|
|
}
|
|
.results-table th,
|
|
.results-table td {
|
|
font-size: 9px;
|
|
padding: 14px 12px;
|
|
}
|
|
.faq-item h4,
|
|
.faq-item p {
|
|
font-size: 9px;
|
|
}
|
|
.selected-title {
|
|
font-size: 14px;
|
|
}
|
|
.selected-meta {
|
|
font-size: 9px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="hero">
|
|
<div class="hero-top">
|
|
<h1>WARIO SYNTH <span class="version-tag">v1.3</span></h1>
|
|
<button id="faqBtnTop" class="faq-link" type="button">FAQ</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<input type="text" id="songInput" placeholder="Search for a song…" />
|
|
<button id="searchBtn" class="primary">Search</button>
|
|
</div>
|
|
|
|
<div id="status">Ready. Search any song to make a Game Boy version.</div>
|
|
|
|
<div id="resultsSection" class="results-section">
|
|
<div id="chooseHelper" class="chooser-helper" style="display:none;">
|
|
<p>Pick a midi source. Try another if it sounds bad, some work better than others.</p>
|
|
</div>
|
|
|
|
<div class="results-header">
|
|
<h3>Search Results</h3>
|
|
</div>
|
|
<table id="resultsTable" class="results-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Song</th>
|
|
<th class="source-col">Source</th>
|
|
<th class="action-col">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="resultsBody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div id="playerSection" class="player-section">
|
|
<div id="selectedCard" class="selected-card">
|
|
<h2 id="selectedTitle" class="selected-title">No source selected</h2>
|
|
<div id="selectedMeta" class="selected-meta">Search for a song, then choose a source to use.</div>
|
|
|
|
|
|
<div class="selected-actions" id="preGenActions">
|
|
<button id="motifBtn" class="primary" disabled>Generate music</button>
|
|
</div>
|
|
|
|
<div class="selected-support" id="preGenSupport">
|
|
<div class="preview-inline">
|
|
<button id="previewBtn" type="button" class="linklike" disabled>Preview original MIDI</button>
|
|
<span id="previewState" class="preview-state">Previewing…</span>
|
|
<button id="previewStopBtn" type="button" class="linklike" style="display:none;">Stop preview</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="iosAudioBanner" class="ios-audio-banner" aria-live="polite">
|
|
<div class="row">
|
|
<div class="copy">
|
|
<strong>iPhone:</strong> check not on silent, volume up, Focus mode off. Then tap:
|
|
</div>
|
|
<button id="enableAudioBtn" type="button">Enable Audio</button>
|
|
</div>
|
|
<div id="iosAudioState" class="state">Audio: …</div>
|
|
</div>
|
|
|
|
<!-- generated only: artifact playback + sharing -->
|
|
<div id="generatedBlock" style="display:none;">
|
|
<div style="display:flex; justify-content:center; margin-top: 14px; margin-bottom: 14px;">
|
|
<button id="playPauseBtn" class="primary" type="button">Play</button>
|
|
</div>
|
|
|
|
<div class="motif-progress-container" id="motifProgressContainer" style="display: none;">
|
|
<div class="progress-time">
|
|
<span id="motifCurrentTime">0:00</span>
|
|
<span id="motifDuration">0:00</span>
|
|
</div>
|
|
<div class="progress-bar-wrapper">
|
|
<input type="range" id="motifProgressBar" class="progress-bar" min="0" max="100" value="0" step="0.1" />
|
|
<div class="progress-fill" id="motifProgressFill"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin-top: 14px; display: flex; gap: 12px; flex-wrap: wrap;">
|
|
<button id="copyLinkBtn" type="button" disabled>Copy link</button>
|
|
<button id="shareToXBtn" type="button" disabled>Share to X</button>
|
|
</div>
|
|
<div id="shareFallback" style="display: none; margin-top: 12px;">
|
|
<p style="font-size: 8px; margin: 0 0 8px 0; color: var(--gb-light);">Sorry, copy doesn't work on your device. Here's the link:</p>
|
|
<input id="shareFallbackInput" type="text" readonly style="width: 100%; padding: 8px; font-size: 8px; font-family: 'Press Start 2P', monospace; background: var(--gb-darkest); color: var(--gb-lightest); border: 2px solid var(--gb-light);" />
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="footer">
|
|
<button id="newSearchBtn" class="faq-link" type="button" style="display:none;">New search</button>
|
|
</div>
|
|
|
|
<div class="version-history">
|
|
<details>
|
|
<summary>Version History</summary>
|
|
<div class="version-content">
|
|
<div class="version-item">
|
|
<strong>How it works:</strong> The Wario Synthesis Engine analyses MIDI files to identify melody, bass, and chord tracks. It then resynthesises each part using Web Audio oscillators tuned to mimic the Game Boy's 4-channel sound chip: two pulse wave channels, one wave channel, and one noise channel. All processing runs client-side in your browser - no server-side audio generation. Built with TypeScript, Vite, and Claude Code.
|
|
</div>
|
|
<div class="version-divider"></div>
|
|
<div class="version-item">
|
|
<strong>v1.3</strong> - Share to X, light Game Boy palette, iOS audio fixes, local font hosting
|
|
</div>
|
|
<div class="version-item">
|
|
<strong>v1.2</strong> - Short share links with dynamic previews, copy link button, improved MIDI selection
|
|
</div>
|
|
<div class="version-item">
|
|
<strong>v1.1</strong> - Search MIDI files, generate Game Boy style music, basic playback with progress bar
|
|
</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<div class="footer-mascot">
|
|
<img src="/wario-sprite.png" alt="Wario" class="wario-sprite" />
|
|
</div>
|
|
|
|
<div id="faqModalBackdrop" class="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="faqTitle">
|
|
<div class="modal" role="document">
|
|
<header>
|
|
<h3 id="faqTitle">FAQ</h3>
|
|
<div class="spacer"></div>
|
|
<button id="faqCloseBtn" type="button">Close</button>
|
|
</header>
|
|
<main>
|
|
<div class="faq-grid">
|
|
<div class="faq-item">
|
|
<h4>How it works</h4>
|
|
<p>Search a song, pick a MIDI source, hit Generate. The Wario Synthesis Engine analyses the MIDI and resynthesises it using Web Audio oscillators tuned to mimic the Game Boy's 4-channel sound chip. All processing runs in your browser.</p>
|
|
</div>
|
|
<div class="faq-item">
|
|
<h4>Why "Enable Audio" on iPhone?</h4>
|
|
<p>Apple blocks sound until you tap something. Hit the button once and you're good. Still silent? Check your ringer switch.</p>
|
|
</div>
|
|
<div class="faq-item">
|
|
<h4>About</h4>
|
|
<p>A non-commercial project by <a href="https://x.com/b1rdmania" target="_blank" rel="noopener noreferrer">@b1rdmania</a> for lols. Please don't sue me. Built with Claude Code.</p>
|
|
<p style="margin-top: 8px;"><a href="https://github.com/b1rdmania/motif" target="_blank" rel="noopener noreferrer">GitHub repo</a></p>
|
|
</div>
|
|
<div class="faq-item">
|
|
<h4>Ps. I love Claude Code.</h4>
|
|
<img src="/wario.png" alt="Wario moment" style="margin-top: 8px; max-width: 100%; opacity: 0.85;" />
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module" src="/src/main.ts"></script>
|
|
</body>
|
|
</html> |