Skip to content

Commit 352be19

Browse files
author
g
committed
Migrate from Node.js to Bun runtime for improved performance
- Update GitHub Actions to use Bun setup and bun install commands - Replace package-lock.json with bun.lockb - Update Next.js config from .ts to .js for compatibility - Change font from Geist to Inter for Next.js 14 compatibility - Add client-side checks for SSR compatibility - Remove unnecessary dependencies (@types/node, tone, recharts) - Update gitignore to include bun.lockb and debug logs - Add version display to footer component
1 parent 59e469f commit 352be19

13 files changed

Lines changed: 526 additions & 2670 deletions

File tree

.github/workflows/nextjs.yml

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,10 @@ jobs:
3131
steps:
3232
- name: Checkout
3333
uses: actions/checkout@v4
34-
- name: Detect package manager
35-
id: detect-package-manager
36-
run: |
37-
if [ -f "${{ github.workspace }}/yarn.lock" ]; then
38-
echo "manager=yarn" >> $GITHUB_OUTPUT
39-
echo "command=install" >> $GITHUB_OUTPUT
40-
echo "runner=yarn" >> $GITHUB_OUTPUT
41-
exit 0
42-
elif [ -f "${{ github.workspace }}/package.json" ]; then
43-
echo "manager=npm" >> $GITHUB_OUTPUT
44-
echo "command=ci" >> $GITHUB_OUTPUT
45-
echo "runner=npx --no-install" >> $GITHUB_OUTPUT
46-
exit 0
47-
else
48-
echo "Unable to determine package manager"
49-
exit 1
50-
fi
51-
- name: Setup Node
52-
uses: actions/setup-node@v4
34+
- name: Setup Bun
35+
uses: oven-sh/setup-bun@v2
5336
with:
54-
node-version: "20"
55-
cache: ${{ steps.detect-package-manager.outputs.manager }}
37+
bun-version: latest
5638
- name: Setup Pages
5739
uses: actions/configure-pages@v5
5840
with:
@@ -67,14 +49,14 @@ jobs:
6749
path: |
6850
.next/cache
6951
# Generate a new cache whenever packages or source files change.
70-
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
52+
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
7153
# If source files changed but packages didn't, rebuild from a prior cache.
7254
restore-keys: |
73-
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
55+
${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-
7456
- name: Install dependencies
75-
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
57+
run: bun install --frozen-lockfile
7658
- name: Build with Next.js
77-
run: ${{ steps.detect-package-manager.outputs.runner }} next build
59+
run: bun run build
7860
- name: Upload artifact
7961
uses: actions/upload-pages-artifact@v3
8062
with:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
!.yarn/releases
1111
!.yarn/versions
1212

13+
# bun
14+
/bun.lockb
15+
1316
# testing
1417
/coverage
1518

@@ -29,6 +32,7 @@ npm-debug.log*
2932
yarn-debug.log*
3033
yarn-error.log*
3134
.pnpm-debug.log*
35+
bun-debug.log*
3236

3337
# env files (can opt-in for committing if needed)
3438
.env*

AGENTS.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Binaural Beat Player - Development Guide
2+
3+
## Project Overview
4+
A Next.js-based binaural beat player with Web Audio API integration, migrated from Node.js to Bun runtime for improved performance and reduced bundle size.
5+
6+
## Technology Stack
7+
- **Runtime**: Bun (>=1.0.0)
8+
- **Framework**: Next.js 14.2.15 with React 18.3.1
9+
- **Styling**: Tailwind CSS v4
10+
- **Audio**: Web Audio API
11+
- **Icons**: Lucide React & React Icons
12+
- **Deployment**: GitHub Pages (static export)
13+
14+
## Development Commands
15+
16+
### Core Commands
17+
```bash
18+
# Install dependencies
19+
bun install
20+
21+
# Start development server with Turbopack
22+
bun run dev
23+
24+
# Build for production
25+
bun run build
26+
27+
# Start production server
28+
bun run start
29+
30+
# Run linting
31+
bun run lint
32+
33+
# Deploy to GitHub Pages
34+
bun run deploy
35+
```
36+
37+
## Key Features
38+
- **Binaural Beat Generation**: Real-time audio synthesis using Web Audio API
39+
- **Pattern Mode**: JSON-based frequency sequences with timing control
40+
- **Manual Mode**: Direct frequency control with range sliders (1-600 Hz)
41+
- **Visualizer**: Real-time waveform visualization using Canvas API
42+
- **Volume Control**: Frequency-aware volume adjustment
43+
- **Responsive Design**: Mobile-first approach with Tailwind CSS
44+
45+
## Project Structure
46+
```
47+
├── app/ # Next.js App Router
48+
│ ├── page.tsx # Main binaural player component
49+
│ ├── layout.tsx # Root layout
50+
│ └── globals.css # Global styles
51+
├── components/ # Reusable components
52+
│ └── Footer.tsx # Footer with donate links
53+
├── bineural-songs/ # Predefined patterns
54+
│ ├── ai-gemini.json # AI-focused pattern
55+
│ ├── creative.json # Creativity enhancement
56+
│ ├── intro.json # Introduction pattern
57+
│ ├── relaxing.json # Relaxation pattern
58+
│ └── scala.json # Scala tuning pattern
59+
└── public/ # Static assets
60+
```
61+
62+
## Audio Implementation Details
63+
64+
### Web Audio API Architecture
65+
- **AudioContext**: Main audio processing context
66+
- **OscillatorNodes**: Generate left/right channel frequencies
67+
- **StereoPannerNodes**: Channel separation (-1 left, +1 right)
68+
- **GainNode**: Volume control with frequency-based scaling
69+
70+
### Frequency Scaling
71+
Volume automatically adjusts based on average frequency:
72+
- 1 Hz: 100% volume
73+
- 600 Hz: 30% volume
74+
- Linear interpolation between endpoints
75+
76+
### Pattern Format
77+
```json
78+
{
79+
"pattern": [
80+
[left_frequency, right_frequency, duration_ms],
81+
// ... more steps
82+
]
83+
}
84+
```
85+
86+
## Migration Notes (Node.js → Bun)
87+
88+
### Removed Dependencies
89+
- `@types/node`: No longer needed with Bun's built-in Node.js compatibility
90+
- `tone`: Removed to prevent localStorage SSR issues
91+
- `recharts`: Removed to prevent localStorage SSR issues
92+
- `package-lock.json`: Replaced with `bun.lockb`
93+
94+
### Configuration Changes
95+
- **next.config.js**: Renamed from .ts for compatibility, static export for production
96+
- **package.json**: Added Bun engine requirement
97+
- **.gitignore**: Added `bun.lockb` and `bun-debug.log*`
98+
99+
### Version Compatibility
100+
- Downgraded Next.js from 15.3.1 to 14.2.15 for stability
101+
- Downgraded React from 19 to 18.3.1 for better SSR compatibility
102+
- Changed font from Geist to Inter for Next.js 14 compatibility
103+
104+
### Performance Benefits
105+
- Faster installation times
106+
- Reduced bundle size
107+
- Improved runtime performance
108+
- Built-in TypeScript support
109+
110+
## Development Guidelines
111+
112+
### Code Style
113+
- Use TypeScript with strict mode
114+
- Follow React functional component patterns
115+
- Implement proper cleanup in useEffect hooks
116+
- Use semantic HTML with ARIA labels
117+
118+
### Audio Best Practices
119+
- Always handle AudioContext state changes (suspended/resumed)
120+
- Implement proper oscillator cleanup
121+
- Use smooth transitions (linearRampToValueAtTime)
122+
- Handle browser compatibility gracefully
123+
124+
### State Management
125+
- Keep audio state separate from UI state
126+
- Use refs for persistent audio objects
127+
- Implement proper error boundaries
128+
129+
## Deployment
130+
The app builds as a static site for GitHub Pages:
131+
- Output directory: `out/`
132+
- Base path: `/binaural-beat-player` (production only)
133+
- Automatic .nojekyll file generation
134+
135+
## Browser Compatibility
136+
- Modern browsers with Web Audio API support
137+
- Chrome/Edge: Full support
138+
- Firefox: Full support
139+
- Safari: Partial support (may require user interaction)
140+
141+
## Deployment
142+
The app builds as a static site for GitHub Pages:
143+
- Output directory: `out/`
144+
- Base path: `/binaural-beat-player` (production only)
145+
- Automatic .nojekyll file generation
146+
147+
## Browser Compatibility
148+
- Modern browsers with Web Audio API support
149+
- Chrome/Edge: Full support
150+
- Firefox: Full support
151+
- Safari: Partial support (may require user interaction)
152+
153+
## Troubleshooting
154+
- **Audio not playing**: Check browser autoplay policies
155+
- **Context suspended**: Call `audioContext.resume()` after user interaction
156+
- **Performance issues**: Monitor oscillator cleanup and gain node connections
157+
- **SSR Issues**: Use `typeof window === 'undefined'` checks for browser APIs

app/debug/page.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client"
2+
3+
import { useEffect, useState } from 'react'
4+
5+
export default function DebugPage() {
6+
const [localStorageAvailable, setLocalStorageAvailable] = useState(false)
7+
const [error, setError] = useState<string | null>(null)
8+
9+
useEffect(() => {
10+
try {
11+
if (typeof window !== 'undefined' && 'localStorage' in window) {
12+
setLocalStorageAvailable(true)
13+
localStorage.getItem('test')
14+
} else {
15+
setError('localStorage not available')
16+
}
17+
} catch (e) {
18+
setError(e instanceof Error ? e.message : 'Unknown error')
19+
}
20+
}, [])
21+
22+
return (
23+
<div className="min-h-screen bg-gray-900 text-white p-8">
24+
<h1 className="text-2xl font-bold mb-6">Debug Page</h1>
25+
<p>localStorage available: {localStorageAvailable ? 'Yes' : 'No'}</p>
26+
{error && <p className="text-red-500">Error: {error}</p>}
27+
</div>
28+
)
29+
}

app/layout.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import type { Metadata } from "next";
2-
import { Geist, Geist_Mono } from "next/font/google";
2+
import { Inter } from "next/font/google";
33
import "./globals.css";
44
import Footer from "@/components/Footer"; // Import the Footer component
55

6-
const geistSans = Geist({
7-
variable: "--font-geist-sans",
8-
subsets: ["latin"],
9-
});
10-
11-
const geistMono = Geist_Mono({
12-
variable: "--font-geist-mono",
6+
const inter = Inter({
7+
variable: "--font-inter",
138
subsets: ["latin"],
9+
preload: false,
1410
});
1511

1612
export const metadata: Metadata = {
@@ -27,7 +23,7 @@ export default function RootLayout({
2723
<html lang="en" suppressHydrationWarning>
2824
{/* Add flex styles to body for sticky footer */}
2925
<body
30-
className={`${geistSans.variable} ${geistMono.variable} antialiased flex flex-col min-h-screen`}
26+
className={`${inter.variable} antialiased flex flex-col min-h-screen`}
3127
suppressHydrationWarning
3228
>
3329
<main className="flex-grow">{children}</main> {/* Wrap children in main for better structure */}

app/page.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const BinauralPlayer: React.FC = () => {
4747
const animationPhaseRef = useRef<number>(0);
4848

4949
useEffect(() => {
50+
// Only initialize audio on client side
51+
if (typeof window === 'undefined') return;
52+
5053
const initAudio = () => {
5154
if (!audioContextRef.current) {
5255
audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)();
@@ -226,8 +229,8 @@ const BinauralPlayer: React.FC = () => {
226229
}, []);
227230

228231
useEffect(() => {
229-
// Ensure canvas logic runs only when mounted and playing
230-
if (!isMounted || !isPlaying) return;
232+
// Ensure canvas logic runs only when mounted and playing and on client side
233+
if (!isMounted || !isPlaying || typeof window === 'undefined') return;
231234

232235
const canvas = canvasRef.current;
233236
if (!canvas) return;
@@ -492,4 +495,4 @@ const BinauralPlayer: React.FC = () => {
492495
);
493496
};
494497

495-
export default BinauralPlayer;
498+
export default BinauralPlayer;

app/simple-page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"use client"
2+
3+
export default function HomePage() {
4+
return (
5+
<div className="min-h-screen bg-gray-900 text-white p-8">
6+
<h1 className="text-2xl font-bold mb-6">Simple Test Page</h1>
7+
<p>This is a test to see if the localStorage error persists.</p>
8+
</div>
9+
)
10+
}

app/test/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"use client"
2+
3+
export default function TestPage() {
4+
return <div className="min-h-screen bg-gray-900 text-white p-8">Test Page</div>
5+
}

0 commit comments

Comments
 (0)