From 5ce02498c1dabd035cd5ff275ef96b9b7b76379d Mon Sep 17 00:00:00 2001 From: Jens Mohrmann Date: Sat, 27 Dec 2025 22:46:58 +0100 Subject: [PATCH] Readme and technical documentation --- README.md | 105 +++++++++++++---------------- TECHNICAL_REALIZATION.md | 48 +++++++++++++ src/App.css | 22 ++++++ src/App.tsx | 11 +++ src/components/OpenSourceModal.tsx | 48 +++++++++++++ src/styles/OpenSourceModal.css | 48 +++++++++++++ 6 files changed, 222 insertions(+), 60 deletions(-) create mode 100644 TECHNICAL_REALIZATION.md create mode 100644 src/components/OpenSourceModal.tsx create mode 100644 src/styles/OpenSourceModal.css diff --git a/README.md b/README.md index d2e7761..c9cae0d 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,58 @@ -# React + TypeScript + Vite +# Sheet Music Trainer -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +A web-based application designed to help musicians practice sight-reading and ear training. It listens to your instrument (or voice) via the microphone and gives real-time feedback. -Currently, two official plugins are available: +[Live Demo](https://9x.github.io/sheetmusictrainer/) (If available) -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +## Features -## React Compiler +- **Sight Reading Mode**: Read notes from the interactive staff and play them on your instrument. The app listens and confirms when you hit the correct note. +- **Ear Training Mode**: Listen to a reference note and try to reproduce it. +- **Real-time Pitch Detection**: Uses your device's microphone to detect notes instantly. +- **Instrument Support**: Optimized for Guitar and Piano, with configurable tunings for guitar. +- **Virtual Instruments**: On-screen interactive guitar fretboard and piano keys for visual reference or touch input. +- **Customizable**: Adjust difficulty (range, accidentals), key signatures, rhythm/metronome settings, and more. +- **Zen Mode**: Distraction-free practice interface. -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). +## Getting Started -## Expanding the ESLint configuration +### Prerequisites -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: +- Node.js (v18 or higher recommended) +- npm -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... +### Installation - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, +1. Clone the repository: + ```bash + git clone https://github.com/9x/sheetmusictrainer.git + cd sheetmusictrainer + ``` - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` +2. Install dependencies: + ```bash + npm install + ``` -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: +3. Run the development server: + ```bash + npm run dev + ``` -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' +4. Open your browser and navigate to the local URL provided (usually `http://localhost:5173`). -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` +## Technical Details + +100% vibe coded using Google Antigravity and Gemini 3 pro. + +This project is built with: +- React + TypeScript +- Vite +- VexFlow (for music notation) +- Pitchfinder (for audio detection) + +For more details on the architecture, see [TECHNICAL_REALIZATION.md](./TECHNICAL_REALIZATION.md). + +## License + +MIT diff --git a/TECHNICAL_REALIZATION.md b/TECHNICAL_REALIZATION.md new file mode 100644 index 0000000..09aaa43 --- /dev/null +++ b/TECHNICAL_REALIZATION.md @@ -0,0 +1,48 @@ +# Technical Realization + +## Architecture Overview + +The **Sheet Music Trainer** is a client-side Single Page Application (SPA) built with **React** and **TypeScript**, powered by **Vite**. It is designed to run entirely in the browser without a backend server, allowing for low-latency audio processing and interactivity. + +### Key Architectural Choices + +1. **Component-Based UI**: + - The application is structured into reusable components (e.g., `SheetMusic`, `Fretboard`, `PianoKeys`, `Controls`) to maintain separation of concerns. + - State management is primarily handled via React's `useState` and `useReducer` at the `App` component level, with props drilling for simpler hierarchies. + +2. **Audio Processing**: + - **Pitch Detection**: Utilizes the `pitchfinder` library (YIN algorithm) to detect pitch from the user's microphone in real-time. This processing happens in a dedicated hook/worker to keep the main thread responsive. + - **Audio Synthesis**: Uses standard Web Audio API for generating simple tones (sine/triangle waves) for playback and feedback. + +3. **Music Rendering**: + - **VexFlow**: The standard library for rendering music notation on the web. It is used in the `SheetMusic` component to draw the staff, notes, clefs, and key signatures dynamically based on the current state. + +4. **Responsiveness**: + - The application uses CSS variables and media queries to adapt to different screen sizes, with specific optimizations for mobile landscape mode to support instrument practice on tablets and phones. + +## Frameworks and Libraries + +### Core +* **[React](https://react.dev/)**: The library for web and native user interfaces. +* **[TypeScript](https://www.typescriptlang.org/)**: Strongly typed JavaScript for safer development. +* **[Vite](https://vitejs.dev/)**: Next Generation Frontend Tooling for fast development and building. + +### Audio & Music +* **[VexFlow](https://www.vexflow.com/)**: A JavaScript library for rendering music notation and guitar tablature. +* **[Pitchfinder](https://github.com/peterkhayes/pitchfinder)**: A collection of pitch detection algorithms for Javascript. used for detecting the note played by the user. + +### UI & Icons +* **[Lucide React](https://lucide.dev/)**: A clean and consistent icon library for the interface. + +## Directory Structure + +``` +src/ +├── components/ # Reusable UI components (SheetMusic, Fretboard, etc.) +├── hooks/ # Custom React hooks (usePitchDetector, useAudioPlayer, etc.) +├── music/ # Music logic, tuning definitions, and note utilities +├── styles/ # Global styles and component-specific CSS +├── assets/ # Static assets +├── App.tsx # Main application logic and layout +└── main.tsx # Entry point +``` diff --git a/src/App.css b/src/App.css index 6de08e0..57e5dff 100644 --- a/src/App.css +++ b/src/App.css @@ -511,6 +511,28 @@ button { color: var(--color-text-muted); } +.link-button { + background: none; + border: none; + padding: 0; + color: var(--color-primary); + cursor: pointer; + font-family: inherit; + font-size: inherit; + text-decoration: none; + transition: color 0.2s; +} + +.link-button:hover { + color: var(--color-primary-dark); + text-decoration: underline; +} + +.separator { + margin: 0 8px; + color: #666; +} + .app-subtitle a { color: var(--color-primary); text-decoration: none; diff --git a/src/App.tsx b/src/App.tsx index 90aa398..2d14657 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useMemo } from 'react'; import { SheetMusic } from './components/SheetMusic'; import { Controls, type AppSettings } from './components/Controls'; import { SettingsModal } from './components/SettingsModal'; +import { OpenSourceModal } from './components/OpenSourceModal'; import { usePitchDetector } from './hooks/usePitchDetector'; import { useMetronome } from './hooks/useMetronome'; import { useAudioPlayer } from './hooks/useAudioPlayer'; @@ -66,6 +67,7 @@ function App() { const [revealed, setRevealed] = useState(false); const [virtualNote, setVirtualNote] = useState(null); const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [isOpenSourceModalOpen, setIsOpenSourceModalOpen] = useState(false); const currentTuning = TUNINGS[settings.tuningId]; const currentInstrumentDef = INSTRUMENT_DEFINITIONS[settings.instrument]; @@ -575,6 +577,10 @@ function App() { currentPitch={pitchData ? { note: pitchData.note, cents: pitchData.cents } : null} />
+ + + GitHub + jensmohrmann.de
@@ -633,6 +639,11 @@ function App() { settings={settings} onUpdateSettings={setSettings} /> + + setIsOpenSourceModalOpen(false)} + /> ); } diff --git a/src/components/OpenSourceModal.tsx b/src/components/OpenSourceModal.tsx new file mode 100644 index 0000000..904767f --- /dev/null +++ b/src/components/OpenSourceModal.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { X } from 'lucide-react'; +import '../styles/OpenSourceModal.css'; + +interface OpenSourceModalProps { + isOpen: boolean; + onClose: () => void; +} + +const LIBRARIES = [ + { name: 'React', url: 'https://react.dev/', description: 'The library for web and native user interfaces' }, + { name: 'TypeScript', url: 'https://www.typescriptlang.org/', description: 'JavaScript with syntax for types' }, + { name: 'Vite', url: 'https://vitejs.dev/', description: 'Next Generation Frontend Tooling' }, + { name: 'VexFlow', url: 'https://www.vexflow.com/', description: 'Music notation rendering for the web' }, + { name: 'Pitchfinder', url: 'https://github.com/peterkhayes/pitchfinder', description: 'Pitch detection algorithms' }, + { name: 'Lucide React', url: 'https://lucide.dev/', description: 'Beautiful & consistent icon toolkit' }, +]; + +export const OpenSourceModal: React.FC = ({ isOpen, onClose }) => { + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()}> +
+

Open Source Libraries

+ +
+ +
+

This project relies on these amazing open source libraries:

+ +
+
+
+ ); +}; diff --git a/src/styles/OpenSourceModal.css b/src/styles/OpenSourceModal.css new file mode 100644 index 0000000..432202a --- /dev/null +++ b/src/styles/OpenSourceModal.css @@ -0,0 +1,48 @@ +.open-source-modal { + max-width: 500px; + width: 90%; +} + +.library-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 16px; +} + +.library-item { + display: flex; + flex-direction: column; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 12px; +} + +.library-item:last-child { + border-bottom: none; +} + +.library-name { + font-weight: 600; + color: #646cff; + /* Vite purple or accent color */ + text-decoration: none; + font-size: 1.1rem; + margin-bottom: 4px; +} + +.library-name:hover { + text-decoration: underline; +} + +.library-desc { + color: #ccc; + font-size: 0.9rem; +} + +@media (prefers-color-scheme: light) { + .library-item { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + } +} \ No newline at end of file