- Svelte 54%
- TypeScript 42.3%
- CSS 3%
- JavaScript 0.4%
- HTML 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Uncommittete Änderungen gesichert (Sammel-Commit), damit der Stand auf Forgejo liegt — ggf. noch halbfertige Arbeit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nis4Pzds5rZXdAQ8VBxKqP |
||
| .claude | ||
| backend | ||
| src | ||
| static | ||
| .gitignore | ||
| .npmrc | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.lib.ts | ||
| vite.config.ts | ||
earTastic AI
Eine vollständig neu geschriebene, moderne Webapp-Version der Gehörbildungs-App earTastic Pro (ursprünglich 2014/15 als Cordova-App entstanden). Dieses Repo ersetzt jQuery + RequireJS + Raphael + FullCalendar + Cordova durch einen schlanken, native-platform-freien Stack:
| Bereich | Stack |
|---|---|
| Framework | SvelteKit 2 · Svelte 5 Runes · TypeScript strict |
| Styling | Tailwind CSS v4 (CSS Variables, OKLCH-Theme) |
| Audio | Web Audio API (eigene AudioEngine, kein Media) |
| Notation | VexFlow 5 |
| Persistenz | IndexedDB via idb |
| PWA | vite-plugin-pwa + Workbox |
| Tests | Vitest (Music-Theory-Logik) |
| Build | Vite, Static-Adapter (@sveltejs/adapter-static) |
| Distribution | Plain static hosting (Netlify, Vercel, GH Pages, S3 …) |
| Einbettung | ES-Modul-Bibliothek (build:lib) für fremde Apps |
Nicht nur eine App — auch eine Bibliothek
earTastic lässt sich ohne seine Oberfläche benutzen. npm run build:lib
erzeugt ein ES-Modul mit den Fragen-Generatoren, den Musikdaten, der
AudioEngine und den Beschriftungen — gedacht für Anwendungen, die earTastics
Musiktheorie und Klang brauchen, aber eine eigene Oberfläche mitbringen.
import * as ear from '/eartastic/build/lib/eartastic.js';
ear.configure({ audioBase: '/eartastic/build/audio' }); // Pflicht, s. u.
const q = ear.generateIntervalQuestion({
enabled: ['k3', 'g3', '4', '5', '8'],
mode: 'asc', rangeLow: 24, rangeHigh: 48,
});
await ear.audioEngine.playSequence('juno', q.playOrder, 750);
ear.label(q.key); // → „Quinte"
Erster Abnehmer ist das Gehörbildungs-Widget der Unterrichtstafel
(/var/www/piano): Die Lehrkraft spielt ein Intervall vor, die Klasse
antwortet per Handy. Alles Musikalische daran kommt von hier — ändert sich in
diesem Repo die Musiktheorie, zieht das Widget automatisch mit.
Details, Umfang und die Stolperstellen: Einbetten in andere Anwendungen.
Setup
Diese Maschine hat noch kein Node — installiere zuerst Node ≥ 20:
sudo pacman -S nodejs npm
Dann im Projekt-Root:
npm install
npm run dev # http://localhost:5173
npm run build # build/ enthält das deploybare Static-Bundle
npm run build:lib # build/lib/eartastic.js — Einbettungs-Bibliothek
npm run build:all # beides, in der richtigen Reihenfolge
npm run preview # build lokal mit /preview-Server testen
npm run check # svelte-check + TypeScript
npm test # Vitest (Music-Engine-Tests)
Reihenfolge beachten:
npm run buildleertbuild/. Wer die Bibliothek mit ausliefern will, nimmtnpm run build:all— sonst istbuild/lib/nach dem nächsten Build weg.
Einbetten in andere Anwendungen
Einstieg und Kurzbeispiel stehen oben; hier der Umfang und die drei Stellen, an denen man sonst hängenbleibt.
Der Einstiegspunkt ist src/lib/embed/index.ts, gebaut über
vite.config.lib.ts (getrennt von vite.config.ts, weil dort SvelteKit,
Tailwind und das PWA-Plugin hängen — nichts davon wird gebraucht, und
sveltekit() akzeptiert den Bibliotheks-Einstiegspunkt gar nicht).
Enthalten
| Generatoren | generateIntervalQuestion, generateChordQuestion, generateScaleQuestion samt Fehlertypen |
| Musikdaten | TONES, ALL_INTERVALS, INTERVAL_SEMITONES, CHORDS, SCALES, INSTRUMENTS, … |
| Klang | audioEngine (playTone, playSequence, playChord, preload, playFx) |
| Beschriftungen | label(key, locale), DICTIONARIES, LOCALES — vier Sprachen |
| Konfiguration | configure({ audioBase, volume }), EMBED_API_VERSION |
Nicht enthalten — bewusst: Svelte-Komponenten, Stores, IndexedDB-Zustand, VexFlow. Die einbettende App bringt ihre eigene Oberfläche mit.
Stolperstellen
1. audioBase ist Pflicht, sobald earTastic nicht an der Domain-Wurzel
liegt. Die AudioEngine lud die Samples früher fest von /audio/…. Unter
eartastic.krg.li stimmt das; unter einem Unterpfad wie
/eartastic/build/ bekommt jedes Sample einen 404 — ohne sichtbaren Fehler,
es bleibt einfach still. configure({ audioBase: '…/audio' }) setzt den Pfad
um; der Standard /audio lässt das Verhalten der App unverändert.
2. Die Intervall-Schlüssel sind deutsch abgekürzt, nicht englisch:
'1', k2, g2, k3, g3, '4', tr, '5', k6 … (siehe
INTERVAL_SEMITONES in src/lib/data/music.ts). Ein englisches 'p5' oder
'M3' führt nicht zu einem Fehler, sondern zu einem irreführenden
RangeTooNarrowError: unbekannte Schlüssel fallen bei der
Halbton-Prüfung heraus, und übrig bleibt eine leere Auswahl.
3. build/ ist gitignored und npm run build leert es. Die gebaute
Bibliothek liegt also nicht im Repo, und ein reines npm run build löscht sie
beim nächsten Deployment wieder. Für Installationen, die sie mit ausliefern:
npm run build:all.
Verzeichnis-Layout
src/
├── app.css # Tailwind v4 Tokens + Komponenten-Klassen
├── app.html # HTML-Shell
├── lib/
│ ├── audio/AudioEngine.ts # Web Audio Fassade + Sample-Cache
│ ├── audio/Tuner.ts # 440 Hz Sinus-Oszillator
│ ├── audio/Metronome.ts # Look-Ahead-Scheduler (Chris-Wilson-Pattern)
│ ├── components/ # Header, Toaster, Piano, Heatmap, AnswerScreen
│ ├── data/music.ts # Typisierter Datensatz aus data.json
│ ├── diary/practiceTimer.ts # visibilitychange-gesteuerter Sessions-Tracker
│ ├── i18n/ # DE/EN/FR/ES-Dictionary + Store
│ ├── music/intervals.ts # Generator + Test
│ ├── music/chords.ts # Generator
│ ├── music/scales.ts # Generator
│ ├── notation/Stave.svelte # VexFlow-Wrapper
│ ├── settings/store.ts # Settings-Store + IndexedDB-Persistenz
│ ├── stats/store.ts # Stats-Counter
│ └── utils/{db,wakeLock,vibrate}.ts
├── routes/
│ ├── +layout.svelte # globaler Header, Toaster, Sample-Vorladung
│ ├── +page.svelte # Overview
│ ├── exercises/{intervals,chords,scales,functions,melodies}/+page.svelte
│ ├── theory/{intervals,chords,scales,hauptstufen,notenlesen,klaviatur}/+page.svelte
│ ├── levels/+page.svelte # Übungen nach Lernstufe
│ ├── admin/levels/+page.svelte # Level-Verwaltung (PocketBase)
│ ├── piano/+page.svelte
│ ├── metronome/+page.svelte
│ ├── stats/+page.svelte
│ ├── diary/+page.svelte # Heatmap (12 Wochen)
│ └── settings/+page.svelte
static/
├── audio/{juno,nylon,homechurch}/ # 60+ WAV-Samples pro Instrument
├── audio/fx/ # 440hz.mp3, welcome.mp3 etc.
├── img/composers/ # 17 JPGs für Antwort-Screens
├── favicon.svg
└── icons/ # PWA-Icons (siehe icons/README.md)
Was funktioniert
- Overview, Settings (vollständig, inkl. Tonumfang, Instrumenten- und
Intervall-/Akkord-/Skalenauswahl), Stats, Diary (Heatmap, Aggregation),
Piano (60 Tasten, Sample-Wiedergabe via Web Audio, optionale Vibration,
Stop-on-release), Metronom (Look-Ahead, akzentuierter Downbeat),
Stimmgabel (440 Hz Sinus über
OscillatorNode), Theorie-Seiten (Hör-Demo aller Intervalle/Akkorde/Skalen ausgehend von C). - Intervall-Übung end-to-end: 4 Modi (asc / desc / sim / sim+Noten), Antwort- auswahl per Multiple-Choice, Stats werden persistiert, Vibration, Wake-Lock, Practice-Timer mit Pause/Resume.
- Akkord-Übung und Skalen-Übung funktional analog zu Intervallen.
- i18n DE/EN/FR/ES — Locale persistiert in
localStorage. - PWA: Manifest, Service Worker mit Cache-First-Strategie für Audio-Samples.
- Sample-Vorladung (
src/lib/audio/preload.ts): Beim Start werden alle 61 Töne des aktiven Instruments plus die FX-Sounds geladen; beim ersten Antippen dekodiertAudioEngine.unlock()sie in einem Zug. Zwei Stufen, weil Autoplay-Regeln keinen AudioContext ohne Nutzer-Interaktion erlauben. Die übrigen Instrumente wandern danach still in den Cache — bewusst ohne Dekodieren, alle drei Sätze zusammen wären einige Dutzend Megabyte im Speicher.
Erweiterungspunkte
| Was | Wie |
|---|---|
| Adaptive Aufgabenwahl | Generatoren in src/lib/music/{intervals,chords,scales}.ts würfeln gleichverteilt. Gewichtung nach Trefferquote aus src/lib/stats/store.ts einziehen. |
| Übungssitzung mit festem Umfang | Aufgabenzähler in den Übungsseiten + Ergebnisbildschirm neben src/lib/components/AnswerScreen.svelte. |
| Tastatur-Shortcuts | Bisher kein Keyboard-Handling in der App; gemeinsamer Helfer für 1–9, Leertaste (erneut abspielen), Enter (weiter). |
| Level-Fortschritt | /levels ist eine Linkliste; Versuche/Bestquote pro Level in kv (siehe utils/db.ts) und Rückkanal aus den Übungsseiten. |
| Charts in Stats | chart.js oder eigenes SVG-Bar-Chart in src/lib/components/Bars.svelte. |
| Pieces / Komponisten-Diary | Eigene IDB-Tabelle pieces in utils/db.ts ergänzen, Route /diary/pieces. |
Was bewusst weggelassen wurde
- Cordova/PhoneGap,
Media-Plugin,device.platform,backbutton, alles was anwindow.analytics,navigator.appodercordova.jshing. - jQuery, Raphael, FullCalendar, OwlCarousel, Modernizr, RequireJS, FastClick — durch Svelte-Komponenten und Web-Plattform-APIs ersetzt.
- Google Analytics. Wenn Analytics gewünscht, einfach Plausible/Umami als
<script>-Tag insrc/app.htmleinfügen.