Files
motif/index.html
T
b1rdmania fd422aa084 Make results a chooser and move preview to selected card.
Removes preview controls from search results, adds a 'Choosing a MIDI source' helper block, introduces selected/generated states, and keeps Preview original MIDI as a secondary action inside the selected source card.
2025-12-26 20:38:15 +00:00

1147 lines
38 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>Motif - Procedural Music Synthesis</title>
<!-- OpenGraph / Social Media Preview -->
<meta property="og:title" content="MOTIF">
<meta property="og:description" content="Procedural Music Synthesis from MIDI">
<meta property="og:image" content="/chiptune_blog_piece_1000x.webp">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<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="MOTIF">
<meta name="twitter:description" content="Procedural Music Synthesis from MIDI">
<meta name="twitter:image" content="/chiptune_blog_piece_1000x.webp">
<!-- General Meta -->
<meta name="description" content="Transform MIDI files into retro synthesized variations. Search for songs, preview them, and generate new procedural versions with retro game console vibes.">
<style>
/* Design System: Synthwave - 8px grid, consistent neon accents */
:root {
--color-bg: #0a0a0a;
--color-surface: #141414;
--color-surface-elevated: #1a1a1a;
--color-border: #2a2a2a;
--color-text: #e8e8e8;
--color-text-dim: #999;
--color-accent-primary: #00ff88;
--color-accent-secondary: #ffaa44;
--spacing-unit: 8px;
--radius: 8px;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.5);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6);
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.7);
--glow-primary: 0 0 20px rgba(0, 255, 136, 0.3);
--glow-secondary: 0 0 20px rgba(255, 170, 68, 0.3);
}
* {
box-sizing: border-box;
}
body {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
background:
radial-gradient(1000px 600px at 50% -150px, rgba(0, 255, 136, 0.12), transparent 60%),
radial-gradient(800px 600px at 110% 20%, rgba(255, 170, 68, 0.08), transparent 55%),
var(--color-bg);
color: var(--color-text);
margin: 0;
padding: calc(var(--spacing-unit) * 3);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
.container {
max-width: 960px;
margin: 0 auto;
}
.hero {
text-align: center;
margin: 0 0 calc(var(--spacing-unit) * 4) 0;
position: relative;
}
.hero h1 {
color: var(--color-accent-primary);
margin: 0;
font-size: 2em;
font-weight: 800;
letter-spacing: 0.08em;
text-shadow: var(--glow-primary);
}
.hero .tagline {
margin: calc(var(--spacing-unit) * 1.5) 0 0 0;
color: rgba(255,255,255,0.72);
font-size: 14px;
}
.faq-link {
background: transparent;
border: 1px solid rgba(255,255,255,0.14);
color: rgba(255,255,255,0.85);
padding: 8px 10px;
border-radius: var(--radius);
box-shadow: none;
font-size: 13px;
}
.faq-link:hover:not(:disabled) {
box-shadow: var(--shadow-sm);
border-color: rgba(255,255,255,0.22);
}
.faq-top {
position: absolute;
top: 0;
right: 0;
}
/* FAQ modal */
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.65);
display: none;
align-items: center;
justify-content: center;
padding: 20px;
z-index: 1000;
}
.modal-backdrop.open { display: flex; }
.modal {
width: min(720px, 100%);
background: rgba(20,20,20,0.98);
border: 1px solid rgba(255,255,255,0.12);
border-radius: 12px;
box-shadow: 0 30px 120px rgba(0,0,0,0.75);
overflow: hidden;
}
.modal header {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border-bottom: 1px solid rgba(255,255,255,0.08);
}
.modal header h3 {
margin: 0;
font-size: 14px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(255,255,255,0.9);
}
.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-radius: 999px;
border: 1px solid rgba(255,255,255,0.12);
background: rgba(0,0,0,0.18);
color: rgba(255,255,255,0.82);
text-decoration: none;
font-size: 12px;
line-height: 1;
box-shadow: none;
transition: border-color 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.modal header .faq-header-links a:hover {
border-color: rgba(255,255,255,0.22);
box-shadow: 0 10px 30px rgba(0,0,0,0.45);
background: rgba(0,0,0,0.28);
}
.modal header .faq-header-links a:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(0,255,136,0.25), 0 12px 34px rgba(0,0,0,0.55);
border-color: rgba(0,255,136,0.45);
}
.modal header .faq-header-links .dot {
width: 6px;
height: 6px;
border-radius: 999px;
background: rgba(0,255,136,0.85);
box-shadow: 0 0 14px rgba(0,255,136,0.22);
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: rgba(0,255,136,0.9);
font-weight: 600;
}
@media (max-width: 480px) {
.modal header {
gap: 10px;
padding: 12px 12px;
}
.modal header .faq-header-links a {
padding: 6px 9px;
font-size: 12px;
}
}
.modal header .spacer { margin-left: auto; }
.modal header button {
padding: 8px 10px;
border-radius: 10px;
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.12);
box-shadow: none;
}
.modal main {
padding: 14px 16px 16px 16px;
}
.faq-grid {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.faq-item {
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
padding: 12px 12px;
background: rgba(0,0,0,0.18);
}
.faq-item h4 {
margin: 0 0 6px 0;
font-size: 13px;
color: rgba(0,255,136,0.9);
}
.faq-item p {
margin: 0;
color: rgba(255,255,255,0.75);
font-size: 13px;
line-height: 1.45;
}
.faq-item code {
font-size: 12px;
color: rgba(255,255,255,0.85);
}
.controls {
margin: calc(var(--spacing-unit) * 3) auto calc(var(--spacing-unit) * 2) auto;
display: flex;
gap: calc(var(--spacing-unit) * 1.5);
align-items: stretch;
justify-content: center;
max-width: 720px;
}
button {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
padding: calc(var(--spacing-unit) * 1.5) calc(var(--spacing-unit) * 3);
border-radius: var(--radius);
cursor: pointer;
font-family: inherit;
font-size: 14px;
font-weight: 500;
transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: var(--shadow-sm);
}
button:hover:not(:disabled) {
background: var(--color-surface-elevated);
transform: translateY(-1px);
border-color: #444;
box-shadow: var(--shadow-md);
}
button:active:not(:disabled) {
transform: translateY(0);
box-shadow: var(--shadow-sm);
}
button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
button.primary {
background: var(--color-accent-primary);
color: #000;
border-color: rgba(0,255,136,0.4);
font-weight: 800;
}
button.primary:hover:not(:disabled) {
background: #00dd77;
box-shadow: var(--glow-primary), var(--shadow-md);
}
/* Make Search the primary CTA */
#searchBtn {
padding: 14px 18px;
border-radius: 12px;
}
#status {
margin: calc(var(--spacing-unit) * 3) 0;
padding: calc(var(--spacing-unit) * 2);
background: var(--color-surface);
border-left: 3px solid var(--color-accent-primary);
border-radius: var(--radius);
box-shadow: var(--shadow-md);
font-size: 14px;
min-height: 24px;
}
input[type="text"] {
flex: 1;
background: rgba(0,0,0,0.25);
color: var(--color-text);
border: 1px solid rgba(255,255,255,0.14);
padding: 14px 14px;
border-radius: 12px;
font-family: inherit;
font-size: 16px;
transition: border-color 150ms ease, box-shadow 150ms ease;
}
input[type="text"]:focus {
outline: none;
border-color: rgba(0,255,136,0.45);
box-shadow: 0 0 0 3px rgba(0,255,136,0.18);
}
.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: rgba(0,0,0,0.18);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
box-shadow: var(--shadow-sm);
}
.chooser-helper h4 {
margin: 0 0 6px 0;
font-size: 12px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: rgba(255,255,255,0.85);
}
.chooser-helper p {
margin: 0;
font-size: 13px;
color: rgba(255,255,255,0.68);
line-height: 1.45;
max-width: 72ch;
}
.results-table {
width: 100%;
border-collapse: collapse;
margin: calc(var(--spacing-unit) * 2) 0;
background: var(--color-surface);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow-md);
}
.results-table th,
.results-table td {
padding: calc(var(--spacing-unit) * 2);
text-align: left;
border-bottom: 1px solid var(--color-border);
}
.results-table th {
background: var(--color-surface-elevated);
color: var(--color-accent-primary);
font-weight: 600;
font-size: 12px;
letter-spacing: 0.05em;
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;
transition: background 150ms ease;
}
.results-table tbody tr:hover {
background: rgba(255, 255, 255, 0.05);
}
.results-table tbody tr:last-child td {
border-bottom: none;
}
.results-table tr.selected {
background: rgba(0, 255, 136, 0.12);
}
.results-table tr.selected td:first-child {
border-left: 4px solid var(--color-accent-primary);
padding-left: calc(var(--spacing-unit) * 2 - 4px);
}
@media (max-width: 640px) {
.results-table th,
.results-table td {
padding: calc(var(--spacing-unit) * 1.5);
font-size: 14px;
}
.results-table th {
font-size: 11px;
}
.results-table .source-col {
display: none;
}
.results-table .duration-col {
width: 60px;
}
.results-table .action-col {
width: 100px;
}
}
.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: #333;
border-radius: 2px;
overflow: hidden;
}
.confidence-fill {
height: 100%;
background: linear-gradient(90deg, #ff4444, #ffaa44, #00ff88);
transition: width 0.2s;
}
/* Selected source card */
.selected-card {
margin-top: calc(var(--spacing-unit) * 3);
padding: calc(var(--spacing-unit) * 3);
background: var(--color-surface-elevated);
border: 1px solid rgba(255,255,255,0.10);
border-radius: 12px;
box-shadow: var(--shadow-lg);
}
.selected-title {
margin: 0;
font-size: 22px;
line-height: 1.2;
font-weight: 800;
letter-spacing: 0.01em;
color: var(--color-text);
}
.selected-meta {
margin: 10px 0 0 0;
font-size: 13px;
color: rgba(255,255,255,0.62);
}
.selected-actions {
display: flex;
gap: 12px;
margin-top: 16px;
flex-wrap: wrap;
}
.selected-actions .primary {
flex: 1;
min-width: 180px;
padding: 12px 16px;
}
.selected-actions button:not(.primary) {
flex: 1;
min-width: 160px;
}
.selected-support {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
margin-top: 12px;
}
.selected-support .linklike {
background: transparent;
border: none;
padding: 0;
box-shadow: none;
color: rgba(0,255,136,0.9);
cursor: pointer;
font-size: 13px;
}
.selected-support .linklike:hover {
text-decoration: underline;
}
.preview-inline {
display: inline-flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.preview-state {
font-size: 12px;
color: rgba(255,255,255,0.55);
display: none;
}
.engine-line {
margin-top: 14px;
color: rgba(255,255,255,0.72);
font-size: 13px;
}
details.details-toggle {
margin-top: 12px;
}
details.details-toggle summary {
cursor: pointer;
color: rgba(255,255,255,0.7);
font-size: 12px;
list-style: none;
}
details.details-toggle summary::-webkit-details-marker { display: none; }
.selected-details {
margin-top: 8px;
font-size: 12px;
color: rgba(255,255,255,0.58);
}
.footer {
margin-top: calc(var(--spacing-unit) * 6);
display: flex;
justify-content: center;
}
.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: 1px solid var(--color-border);
border-radius: var(--radius);
box-shadow: var(--shadow-md);
transition: transform 150ms ease, box-shadow 150ms ease;
}
.player-box:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.player-box h4 {
margin: 0 0 calc(var(--spacing-unit)) 0;
color: var(--color-accent-primary);
font-size: 15px;
font-weight: 600;
}
.player-box p {
font-size: 13px;
color: var(--color-text-dim);
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: 1px solid rgba(255, 170, 68, 0.2);
box-shadow: var(--shadow-lg), var(--glow-secondary);
}
.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 {
box-shadow: 0 16px 60px rgba(255, 170, 68, 0.15);
border-color: rgba(255, 170, 68, 0.55);
}
.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-radius: var(--radius);
border: 1px solid rgba(255, 170, 68, 0.22);
background: rgba(0, 0, 0, 0.25);
box-shadow: var(--shadow-sm);
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: 12px;
color: var(--color-text-dim);
line-height: 1.35;
flex: 1;
min-width: 220px;
}
.ios-audio-banner .copy strong {
color: var(--color-text);
font-weight: 600;
}
.ios-audio-banner .state {
margin-top: calc(var(--spacing-unit) * 1);
font-size: 12px;
color: rgba(255, 170, 68, 0.85);
display: none;
}
.ios-audio-banner button {
padding: calc(var(--spacing-unit) * 1.25) calc(var(--spacing-unit) * 2);
border-radius: var(--radius);
border: 1px solid rgba(255, 170, 68, 0.35);
background: rgba(255, 170, 68, 0.12);
color: var(--color-text);
box-shadow: var(--shadow-sm);
font-weight: 600;
}
.ios-audio-banner button:hover:not(:disabled) {
border-color: rgba(255, 170, 68, 0.55);
box-shadow: var(--glow-secondary), var(--shadow-md);
}
.motif-kicker {
font-size: 0.85em;
color: #b6b6b6;
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: 0.9em;
color: #aaa;
}
.volume-control input[type="range"] {
flex: 1;
margin: 0;
}
.motif-progress-container {
margin-top: 8px;
padding-top: 16px;
border-top: 1px solid rgba(255, 170, 68, 0.15);
}
.progress-time {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 0.85em;
color: #8f8f8f;
}
.progress-bar-wrapper {
position: relative;
height: 44px;
display: flex;
align-items: center;
padding: calc(var(--spacing-unit) * 2) 0;
}
.progress-bar {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: transparent;
border-radius: 3px;
outline: none;
cursor: pointer;
position: relative;
z-index: 2;
}
.progress-bar::-webkit-slider-runnable-track {
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.progress-bar::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--color-accent-secondary);
cursor: pointer;
box-shadow: 0 0 8px rgba(255, 170, 68, 0.6), var(--shadow-sm);
margin-top: -7px;
transition: transform 100ms ease;
}
.progress-bar:active::-webkit-slider-thumb {
transform: scale(1.15);
}
.progress-bar::-moz-range-track {
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.progress-bar::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--color-accent-secondary);
cursor: pointer;
border: none;
box-shadow: 0 0 8px rgba(255, 170, 68, 0.6), var(--shadow-sm);
}
.progress-fill {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 6px;
background: linear-gradient(90deg, var(--color-accent-secondary), rgba(255, 170, 68, 0.5));
border-radius: 3px;
pointer-events: none;
z-index: 1;
width: 0%;
transition: width 0.1s linear;
}
/* Use on your website */
.embed-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: calc(var(--spacing-unit) * 3);
box-shadow: var(--shadow-md);
}
.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);
border-radius: calc(var(--radius) + 2px);
background: rgba(0,0,0,0.35);
border: 1px solid rgba(255,255,255,0.08);
overflow: auto;
box-shadow: var(--shadow-sm);
}
.codeblock code {
font-size: 12px;
color: rgba(255,255,255,0.85);
white-space: pre;
}
.embed-hint {
margin-top: calc(var(--spacing-unit) * 1.5);
color: var(--color-text-dim);
font-size: 12px;
}
.embed-hint strong {
color: var(--color-text);
font-weight: 600;
}
.copy-toast {
margin-left: auto;
font-size: 12px;
color: rgba(0,255,136,0.85);
display: none;
}
.engine-selector {
margin-top: 10px;
font-size: 0.9em;
}
.engine-selector label {
color: #aaa;
display: block;
margin-bottom: 5px;
}
.engine-selector select {
background: #333;
color: #e0e0e0;
border: 1px solid #555;
padding: 5px 10px;
font-family: inherit;
width: 100%;
}
</style>
</head>
<body>
<div class="container">
<div class="hero">
<button id="faqBtn" class="faq-link faq-top" type="button">FAQ</button>
<h1>MOTIF</h1>
<p class="tagline"><strong>Procedural music generated from MIDI structure</strong></p>
</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. Enter a song name to search for MIDI files.</div>
<div id="resultsSection" class="results-section">
<div id="chooseHelper" class="chooser-helper" style="display:none;">
<h4>Choosing a MIDI source</h4>
<p>
Different MIDI files capture songs differently. Some will produce better results than others.<br />
Pick a source, generate, and if it sounds off, try another.
</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="duration-col">Length</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 id="selectedExpectation" class="selected-meta" style="margin-top: 8px; color: rgba(255,255,255,0.62);">
Some MIDI files work better than others. If the result sounds off, try another source.
</div>
<div class="selected-actions">
<button id="motifBtn" class="primary" disabled>Generate music</button>
</div>
<div class="selected-support">
<button id="motifStopBtn" type="button" disabled>Stop</button>
<button id="copyLinkBtn" type="button" disabled style="display:none;">Copy link</button>
<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>
<button id="chooseDifferentBtn" type="button" class="linklike">Choose a different source</button>
</div>
<div class="engine-line">Reimagined as classic game-console music using Motifs Wario engine.</div>
<div id="iosAudioBanner" class="ios-audio-banner" aria-live="polite">
<div class="row">
<div class="copy">
<strong>iOS Safari:</strong> tap once to enable audio, then press <strong>Generate</strong>.
</div>
<button id="enableAudioBtn" type="button">Enable Audio</button>
</div>
<div id="iosAudioState" class="state">Audio: …</div>
</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>
<details class="details-toggle">
<summary>Details</summary>
<div id="selectedDetails" class="selected-details"></div>
</details>
</div>
<div class="section" id="embedSection" data-not-live="true" style="display:none;">
<div class="section-header">
<h3 class="section-title">Use on your website <span style="color: rgba(255,255,255,0.5); font-weight: 500;">(Coming soon)</span></h3>
<p class="section-subtitle">Embeddable widget + multiple synth engines.</p>
</div>
<div class="divider"></div>
<div class="embed-card" style="opacity:0.55; filter: grayscale(20%); position:relative;">
<div style="position:absolute; inset:0; background: linear-gradient(90deg, rgba(0,0,0,0.35), rgba(0,0,0,0)); pointer-events:none;"></div>
<div class="embed-row">
<pre class="codeblock"><code id="embedCode">&lt;iframe src=&quot;...&quot;&gt;&lt;/iframe&gt;</code></pre>
<div style="display:flex; flex-direction:column; gap: calc(var(--spacing-unit) * 1.5);">
<button id="copyEmbedBtn" class="primary" type="button" disabled>Copy embed</button>
<span id="copyToast" class="copy-toast">Copied</span>
</div>
</div>
<div class="embed-hint">
<strong>Note:</strong> This is not live yet. Well ship a dedicated <code>/embed</code> widget + style presets next.
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<button id="faqBtnFooter" class="faq-link" type="button">FAQ</button>
</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>
<nav class="faq-header-links" aria-label="FAQ links">
<a href="https://x.com/b1rdmania" target="_blank" rel="noopener noreferrer" aria-label="Open @b1rdmania on X">
<span class="dot" aria-hidden="true"></span>
<span class="label"><span class="handle">@b1rdmania</span><span aria-hidden="true">X</span></span>
</a>
<a href="https://github.com/b1rdmania/motif" target="_blank" rel="noopener noreferrer" aria-label="Open the MOTIF GitHub repository">
<span class="dot" aria-hidden="true"></span>
<span class="label">GitHub</span>
</a>
</nav>
<div class="spacer"></div>
<button id="faqCloseBtn" type="button">Close</button>
</header>
<main>
<div class="faq-grid">
<div class="faq-item">
<h4>What is MOTIF?</h4>
<p>Turn any song into retro <strong>Gameboy-style</strong> synth. Search a track, preview the MIDI, hit Generate.</p>
</div>
<div class="faq-item">
<h4>How does it work?</h4>
<p>We scrape MIDI files from the web, analyse the structure (melody, bass, chords), then resynthesise with an emulation engine. Light touch and runs in any browser through <strong>Web Audio</strong>.</p>
</div>
<div class="faq-item">
<h4>Why "Enable Audio" on iPhone?</h4>
<p>Apple blocks sound until you tap something. Just hit the button once — you're good. Still silent? Check your ringer switch isn't on mute.</p>
</div>
<div class="faq-item">
<h4>Ps. I love Claude Code.</h4>
<img src="/wario.png" alt="Wario moment" style="margin-top: 8px; border-radius: 8px; max-width: 100%; opacity: 0.85;" />
</div>
</div>
</main>
</div>
</div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>