A cross-platform sheet music viewer built with React Native and Expo. Display and interact with MusicXML files using OpenSheetMusicDisplay (OSMD) in a native mobile or web app.
SheetFlow was created to make classical sheet music universally accessible and interactive for musicians on any device. Leveraging modern cross-platform technologies, SheetFlow brings MusicXML files to life, allowing users to browse, view, and interact with high-quality digital scores. The app is designed for music students, teachers, and performers seeking a portable, responsive, and extensible music notation experience.
- Tech Stack: Expo (React Native), TypeScript, React Native WebView, OpenSheetMusicDisplay (OSMD)
- Routing: Modular, file-based routing via Expo Router
- Music Rendering: OSMD integrated seamlessly via WebView for performant, pixel-perfect notation
- State Management: React Context for global state (extensible to Redux/Zustand for larger scale)
- Component Structure: Highly composable, platform-aware components for maximum code reuse
- Cross-Platform Support: Uniform experience across iOS, Android, and Web, with responsive hooks and adaptive UI
- π± Universal Platform Support: iOS, Android, and Web
- π΅ MusicXML Rendering: Professional, interactive sheet music display
- π¨ Powered by OSMD: High-fidelity music notation via OpenSheetMusicDisplay
- πΉ Audio Playback Controls: Play, pause, stop, and loop sheet music with visual cursor tracking
- π Loop Playback: Automatically repeat sheet music for practice sessions
- π― Interactive Cursor: Visual tracking of playback position with customizable colors
- π Zoom Controls: Smooth zoom in/out functionality for detailed viewing
- π Authentication: Google Sign-In, Apple Sign-In, and guest access support
- π Multi-Score Library: Browse classical works and hymns with difficulty ratings
- π·οΈ Difficulty Badges: Easy, Medium, and Hard ratings for each piece
- π² Responsive Design: Mobile-first, but scalable for desktop/tablet
- π§© Easy Extensibility: Add new scores or features with minimal code changes
- Web-Native Integration: Advanced use of React Native WebView to embed and interact with a web-based music notation engine, overcoming cross-platform rendering and communication challenges.
- Audio Playback System: Integrated OSMD playback manager with custom loop functionality, cursor tracking, and responsive control interface.
- Cross-Platform Authentication: Firebase Auth with Google Sign-In, Apple Sign-In, and anonymous access, properly configured for iOS, Android, and Web.
- Performance Optimization: Efficient MusicXML parsing and rendering for large scores, with lazy loading and caching strategies.
- UI Component Library: Leveraging Gluestack UI for consistent, accessible, and responsive components across platforms.
- Responsive Sheet Music Layout: Custom hooks and layout logic ensure readable sheet music on any screen size.
- Modular File Structure: File-based routing and separation of concerns for maintainability and scalability.
SheetFlow on Android
screen-20250901-044203.mp4
The app uses a WebView component to render MusicXML files through OpenSheetMusicDisplay (OSMD), a powerful JavaScript library for music notation. This approach allows us to display complex sheet music in a native environment with minimal performance overhead and maximum visual fidelity.
-
Home Screen (
app/(tabs)/index.tsx):- Browse available sheet music with difficulty ratings
- User authentication status and controls
- Clean, card-based interface with FlatList for performance
-
Sheet Music Display (
app/sheet/index.tsx&components/sheetMusicDisplay/):- MusicXML file loading and parsing
- WebView integration with OSMD
- Cross-platform file handling (web vs native)
- Audio playback controls with loop functionality
- Zoom controls and cursor customization
-
Authentication (
contexts/AuthContext.tsx&app/auth/login.tsx):- Firebase Authentication integration
- Google Sign-In with MaterialIcons branding
- Apple Sign-In (iOS only) with proper branding
- Guest access for quick usage
- Persistent authentication state
Currently included:
- Clementi: Sonatina Op.36 No.1 Part 2 (Andante) - Easy
- Beethoven: An die ferne Geliebte Op.98 (Page 1) - Hard
- Mendelssohn: Op. 98 - Medium
- Traditional: Original Silent Night - Easy
- Traditional: To God Be The Glory - Easy
Each piece includes difficulty ratings (Easy, Medium, Hard) displayed as colored badges. Add more files easily by updating the musicFiles array in app/(tabs)/index.tsx and app/sheet/index.tsx.
- Install dependencies
npm install
- Start the app
Options for running:
npx expo start
Start developing by editing files in the app directory. This project uses file-based routing.
The app supports Google Sign-In, Apple Sign-In, and guest access for user authentication. To enable these features:
-
Set up Firebase Project:
- Go to Firebase Console
- Create a new project or use existing one
- Enable Authentication service
- Add Google and Apple as sign-in providers
- Enable Anonymous authentication for guest access
-
Get Client IDs:
- Go to Project Settings > General tab
- Scroll to "Your apps" section
- Note the Web Client ID from your web app configuration
- For iOS: Add an iOS app or check existing iOS app for Client ID
Update the file config/auth.ts with your actual client IDs:
export const GOOGLE_WEB_CLIENT_ID = 'your-actual-web-client-id.apps.googleusercontent.com';
export const GOOGLE_IOS_CLIENT_ID = 'your-actual-ios-client-id.apps.googleusercontent.com';For Android:
- Download
google-services.jsonfrom Firebase Console - Place it in the root directory of your project
For iOS:
- Download
GoogleService-Info.plistfrom Firebase Console - Add it to your iOS project when building with Xcode
- Google Sign-In: Available on Android and iOS with proper Google branding
- Apple Sign-In: iOS-only with Apple branding (requires actual device for testing)
- Guest Access: Anonymous authentication for quick app exploration
- Persistent Sessions: Users remain logged in across app restarts
- Profile Management: View user email and sign out functionality
- Google Sign-In works on Android and iOS
- Apple Sign-In only works on iOS devices (not simulators in some cases)
- Guest access works on all platforms
- Make sure to test on actual devices for full functionality
For production-ready standalone builds that don't require a Metro bundler:
-
Generate native iOS project:
npx expo prebuild --platform ios --clean
-
Export JavaScript bundle:
npx expo export --platform ios --output-dir ios-build -
Copy bundle to iOS project:
cp ios-build/_expo/static/js/ios/*.hbc ios/sheetflow/main.jsbundle -
Add bundle to Xcode project:
- Open
ios/sheetflow.xcworkspacein Xcode - Right-click on "sheetflow" folder β "Add Files to 'sheetflow'"
- Select
main.jsbundleand ensure it's added to the target
- Open
-
Build and run:
npx expo run:ios --configuration Release --device
-
Configure Android SDK path:
echo "sdk.dir=$ANDROID_HOME" > android/local.properties
-
Build release APK:
cd android && ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a
-
Install on device:
./gradlew installRelease
For cloud-based builds or when local builds are challenging:
First-time setup:
# Install EAS CLI
npm install -g @expo/eas-cli
# Login to Expo
npx eas login
# Configure EAS Build
npx eas build:configureFor Development Builds (with expo-dev-client):
# Install expo-dev-client for development builds
npx expo install expo-dev-client
# Build development client
npx eas build --platform all --profile development
# Or build locally
npx eas build --platform ios --profile development --local
npx eas build --platform android --profile development --localFor Production Builds:
# Build for both platforms
npx eas build --platform all --profile production
# Or build locally
npx eas build --platform ios --profile production --local
npx eas build --platform android --profile production --localNote: Release builds include the JavaScript bundle and run independently of Metro server, making them suitable for testing Firebase Auth persistence and production deployment.
Troubleshooting EAS Build:
- For development builds, ensure
expo-dev-clientis installed:npx expo install expo-dev-client - Use
developmentprofile for development builds, notdebug - Development builds require the Expo Go app or a custom development client
- Production builds are standalone and don't require additional apps
- GitHub Packages Authentication: If using private GitHub packages, configure authentication:
- Create a GitHub Personal Access Token with
read:packagespermission at: https://github.com/settings/tokens - For local development: Set environment variable:
export GITHUB_TOKEN=your-token-here(add to ~/.zshrc for persistence) - For EAS Build: Update
.npmrcfile to include the token directly:@joelkingsley:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=your-actual-token-here - Security Note: The
.npmrcwith direct token should not be committed to version control in production - Verify local access:
npm installshould work without authentication errors - Alternative: Use public npm packages instead of private GitHub packages
- Create a GitHub Personal Access Token with
- Linting: Automated with ESLint and Prettier
- Testing: Unit and integration tests using Jest and React Native Testing Library (planned for future releases)
- CI/CD: Ready for GitHub Actions and Expo EAS for automated builds and deployments
Built and maintained by Joel Kingsley
- Designed and architected the codebase for scalability and maintainability
- Led all aspects of development: UI/UX, integration, performance, and deployment
- Established coding standards and best practices
- Onboarded contributors with clear docs and code reviews
- React Native WebView: For rendering HTML/JavaScript content in the native app
- OpenSheetMusicDisplay: For music notation rendering (loaded via CDN)
- Expo: For cross-platform development & deployment
- Firebase: Authentication and backend services
- Gluestack UI: Comprehensive component library for consistent UI/UX
- React Native Google Sign-In: Google authentication integration
- Expo Apple Authentication: Apple Sign-In for iOS devices