diff --git a/models.html b/models.html
index 9b9c807..3dffbe8 100644
--- a/models.html
+++ b/models.html
@@ -130,13 +130,43 @@
.model-row { display:flex; align-items:center; gap: 10px; flex-wrap: wrap; }
.model-row label { color: rgba(255,255,255,0.7); font-size: 12px; }
- select {
- background: rgba(0,0,0,0.28);
- border: 1px solid rgba(255,255,255,0.12);
- color: rgba(255,255,255,0.9);
+ .model-toggle {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ align-items: center;
+ }
+ .model-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
padding: 10px 12px;
- border-radius: 10px;
- font-family: inherit;
+ border-radius: 999px;
+ border: 1px solid rgba(255,255,255,0.14);
+ background: rgba(0,0,0,0.18);
+ color: rgba(255,255,255,0.86);
+ font-size: 12px;
+ line-height: 1;
+ user-select: none;
+ }
+ .model-btn .dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.35);
+ }
+ .model-btn:hover { border-color: rgba(255,255,255,0.22); box-shadow: 0 14px 40px rgba(0,0,0,0.35); }
+ .model-btn[aria-pressed="true"] {
+ border-color: rgba(0,255,136,0.35);
+ background: linear-gradient(90deg, rgba(0,255,136,0.16), rgba(255,60,245,0.10));
+ }
+ .model-btn[aria-pressed="true"] .dot {
+ background: rgba(0,255,136,0.85);
+ box-shadow: 0 0 14px rgba(0,255,136,0.22);
+ }
+ .model-btn:focus-visible {
+ outline: none;
+ box-shadow: 0 0 0 3px rgba(0,255,136,0.25), 0 14px 40px rgba(0,0,0,0.35);
}
.ios-audio-banner {
@@ -219,13 +249,22 @@
-
-
-
Square/triangle/noise vibe, tight envelope.
+
+
+
+
+
+
+
Chip: square/triangle/saw flavor, tight envelope, crisp attacks.
diff --git a/src/models.ts b/src/models.ts
index c6ba628..dc21925 100644
--- a/src/models.ts
+++ b/src/models.ts
@@ -36,8 +36,9 @@ class ModelsApp {
private motifStopBtn!: HTMLButtonElement;
private motifVolumeSlider!: HTMLInputElement;
- private modelSelect!: HTMLSelectElement;
private modelHint!: HTMLElement;
+ private currentModel: SynthModel = 'nes_gb';
+ private modelButtons: HTMLButtonElement[] = [];
private iosAudioBanner!: HTMLElement;
private enableAudioBtn!: HTMLButtonElement;
@@ -86,8 +87,12 @@ class ModelsApp {
this.motifStopBtn = document.getElementById('motifStopBtn') as HTMLButtonElement;
this.motifVolumeSlider = document.getElementById('motifVolume') as HTMLInputElement;
- this.modelSelect = document.getElementById('modelSelect') as HTMLSelectElement;
this.modelHint = document.getElementById('modelHint')!;
+ this.modelButtons = [
+ document.getElementById('modelBtnPre8') as HTMLButtonElement,
+ document.getElementById('modelBtnNesGb') as HTMLButtonElement,
+ document.getElementById('modelBtnSnes') as HTMLButtonElement,
+ ];
this.nextResultBtn = document.getElementById('nextResultBtn') as HTMLButtonElement;
@@ -116,7 +121,9 @@ class ModelsApp {
this.testEngine?.setVolume(volume);
});
- this.modelSelect.addEventListener('change', () => this.syncModelHint());
+ for (const btn of this.modelButtons) {
+ btn.addEventListener('click', () => this.setModel((btn.dataset.model as SynthModel) || 'nes_gb'));
+ }
this.nextResultBtn.addEventListener('click', () => this.handleNextResult());
@@ -126,9 +133,17 @@ class ModelsApp {
}
private getModel(): SynthModel {
- const v = (this.modelSelect.value || 'nes_gb') as SynthModel;
- if (v === 'pre8bit' || v === 'nes_gb' || v === 'snes_ish') return v;
- return 'nes_gb';
+ return this.currentModel;
+ }
+
+ private setModel(model: SynthModel): void {
+ if (model !== 'pre8bit' && model !== 'nes_gb' && model !== 'snes_ish') return;
+ this.currentModel = model;
+ for (const btn of this.modelButtons) {
+ const isOn = btn.dataset.model === model;
+ btn.setAttribute('aria-pressed', isOn ? 'true' : 'false');
+ }
+ this.syncModelHint();
}
private syncModelHint(): void {