Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dependencies
node_modules/
npm-debug.log
yarn-error.log
package-lock.json
yarn.lock

# Build outputs
dist/
build/
*.min.js
*.min.css

# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Temporary files
tmp/
temp/
*.tmp

# Environment
.env
.env.local

# Logs
logs/
*.log
128 changes: 127 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,127 @@
# strudelStudio
# Strudel Studio 🎵

A web-based editor for coding with [Strudel.cc](https://strudel.cc) REPL - a live coding pattern language for making music.

## Features

- 🎹 **Live Code Editor**: Write and evaluate Strudel patterns in real-time
- 🎨 **Syntax Highlighting**: Dark theme with monospace font for better readability
- ▶️ **Play/Stop Controls**: Easy controls for playing and stopping your patterns
- 📝 **Example Code**: Built-in examples to get you started
- 🖥️ **Console Output**: See evaluation results and errors
- ⌨️ **Keyboard Shortcuts**: Use Ctrl+Enter (or Cmd+Enter on Mac) to evaluate code
- 🌙 **Dark Theme**: Eye-friendly dark theme for extended coding sessions

## Getting Started

### Quick Start (No Installation Required)

Simply open `index.html` in your web browser:

```bash
# Open the file directly
open index.html # macOS
xdg-open index.html # Linux
start index.html # Windows
```

### Using a Local Server

For a better development experience, you can run a local web server:

```bash
# Using Python 3
python3 -m http.server 8080

# Or using npm
npm start
```

Then open your browser to `http://localhost:8080`

## Usage

1. **Write Code**: Type your Strudel code in the editor
2. **Evaluate**: Press `Ctrl+Enter` (or `Cmd+Enter` on Mac) or click the "Play" button
3. **Stop**: Click the "Stop" button to stop playback
4. **Load Example**: Click "Example" to load sample code
5. **Clear**: Use "Clear" to reset the editor

### Example Code

```javascript
// Simple drum pattern
sound("bd sd bd sd").fast(2)

// More complex pattern
stack(
sound("bd*2 sd"),
sound("hh*8").gain(0.3),
note("c3 eb3 g3 bb3").s("sawtooth").lpf(800)
)
```

## Keyboard Shortcuts

- `Ctrl+Enter` / `Cmd+Enter` - Evaluate code
- `Tab` - Insert spaces for indentation

## Technologies Used

- **HTML5/CSS3/JavaScript** - Modern web technologies
- **Strudel** - Live coding pattern language (ready for integration)
- **Vanilla JS** - No framework dependencies, lightweight and fast

## Project Structure

```
strudelStudio/
├── index.html # Main HTML file
├── styles.css # Styling
├── app.js # Application logic
├── package.json # Project metadata
└── README.md # Documentation
```

## Development

The editor is built with vanilla JavaScript and requires no build step. Dependencies can be added via CDN or npm as needed.

### Customization

- **Editor Theme**: Modify the textarea styling in `styles.css` (`.code-textarea` class)
- **Colors**: Customize the CSS variables in `styles.css` (`:root` section)
- **Functionality**: Extend features in `app.js`

## Future Enhancements

- Full Strudel REPL integration with audio playback
- Save/Load patterns to local storage or files
- Multiple editor tabs
- Pattern visualization
- MIDI controller support
- Collaborative editing

## Contributing

Contributions are welcome! Feel free to:
- Report bugs
- Suggest features
- Submit pull requests

## License

MIT License - feel free to use this project for any purpose.

## Credits

Built for the [Strudel](https://strudel.cc) live coding community.

## Resources

- [Strudel Documentation](https://strudel.cc)
- [Strudel Tutorial](https://strudel.cc/learn)

## Security Note

The current implementation uses JavaScript `eval()` for code evaluation. This is suitable for local development and trusted code. For production use with untrusted code, consider implementing a sandboxed evaluation environment.
200 changes: 200 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Strudel Studio - Main Application
// This file handles the editor functionality and Strudel REPL integration

let editor;
let isPlaying = false;
let currentPattern = null;

// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
initializeEditor();
setupEventListeners();
logToConsole('Strudel Studio initialized. Ready to code!', 'success');
});

// Initialize the editor (simple textarea approach)
function initializeEditor() {
editor = document.getElementById('code-editor');

// Add keyboard shortcut support
editor.addEventListener('keydown', function(e) {
// Ctrl+Enter or Cmd+Enter to evaluate
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
evaluateCode();
}

// Tab key inserts spaces instead of changing focus
if (e.key === 'Tab') {
e.preventDefault();
const start = this.selectionStart;
const end = this.selectionEnd;
const value = this.value;
this.value = value.substring(0, start) + ' ' + value.substring(end);
this.selectionStart = this.selectionEnd = start + 2;
}
});
}

// Setup event listeners for buttons
function setupEventListeners() {
document.getElementById('playBtn').addEventListener('click', evaluateCode);
document.getElementById('stopBtn').addEventListener('click', stopCode);
document.getElementById('clearBtn').addEventListener('click', clearEditor);
document.getElementById('exampleBtn').addEventListener('click', loadExample);
document.getElementById('clearConsoleBtn').addEventListener('click', clearConsole);
}

// Evaluate the code in the editor
async function evaluateCode() {
const code = editor.value.trim();

if (!code) {
logToConsole('No code to evaluate', 'error');
return;
}

try {
updateStatus('Evaluating...', 'info');
logToConsole('Evaluating code...', 'info');

// Note: Using eval() for code evaluation
// This is suitable for local development and trusted code
// For production use with untrusted code, implement a sandboxed evaluation environment
// In a full Strudel implementation, this would use Strudel's pattern evaluation API
const result = eval(code);

isPlaying = true;
updatePlayButton();
updateStatus('Playing ♪', 'success');
logToConsole('Code evaluated successfully!', 'success');

if (result !== undefined) {
logToConsole(`Result: ${result}`, 'info');
}
} catch (error) {
logToConsole(`Error: ${error.message}`, 'error');
updateStatus('Error', 'error');
console.error('Evaluation error:', error);
}
}

// Stop the currently playing code
function stopCode() {
if (isPlaying) {
// In a full implementation, this would stop the Strudel pattern
isPlaying = false;
updatePlayButton();
updateStatus('Stopped', 'info');
logToConsole('Playback stopped', 'info');
} else {
logToConsole('Nothing is playing', 'info');
}
}

// Clear the editor content
function clearEditor() {
if (confirm('Clear all code in the editor?')) {
editor.value = '';
logToConsole('Editor cleared', 'info');
}
}

// Load an example
function loadExample() {
const example = `// Strudel Pattern Example
// This creates a rhythmic drum pattern

sound("bd sd bd sd")
.fast(2)

// Try modifying the sounds!
// Available sounds: bd, sd, hh, cp, clap, rim, etc.

// More complex example:
// stack(
// sound("bd*2 sd"),
// sound("hh*8").gain(0.3),
// note("c3 eb3 g3 bb3").s("sawtooth").lpf(800)
// )`;

editor.value = example;
logToConsole('Example loaded', 'success');
}

// Update the play button state
function updatePlayButton() {
const playBtn = document.getElementById('playBtn');
if (isPlaying) {
playBtn.textContent = '▶ Playing...';
playBtn.style.opacity = '0.7';
} else {
playBtn.textContent = '▶ Play';
playBtn.style.opacity = '1';
}
}

// Update status indicator
function updateStatus(message, type = 'info') {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.style.borderColor = getColorForType(type);
}

// Log message to console output
function logToConsole(message, type = 'log') {
const consoleOutput = document.getElementById('console-output');
const logEntry = document.createElement('div');
logEntry.className = `log ${type}`;

const timestamp = new Date().toLocaleTimeString();
logEntry.textContent = `[${timestamp}] ${message}`;

consoleOutput.appendChild(logEntry);
consoleOutput.scrollTop = consoleOutput.scrollHeight;
}

// Clear console output
function clearConsole() {
const consoleOutput = document.getElementById('console-output');
consoleOutput.innerHTML = '';
logToConsole('Console cleared', 'info');
}

// Get color for message type
function getColorForType(type) {
const colors = {
'success': '#10b981',
'error': '#ef4444',
'info': '#6366f1',
'log': '#888'
};
return colors[type] || colors.log;
}

// Override console methods to capture output
const originalConsoleLog = console.log;
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;

console.log = function(...args) {
originalConsoleLog.apply(console, args);
logToConsole(args.join(' '), 'log');
};

console.error = function(...args) {
originalConsoleError.apply(console, args);
logToConsole(args.join(' '), 'error');
};

console.warn = function(...args) {
originalConsoleWarn.apply(console, args);
logToConsole(args.join(' '), 'info');
};

// Note: This is a standalone editor that demonstrates the UI and basic functionality
// To fully integrate with Strudel, you would need to:
// 1. Load the Strudel library (from npm or CDN when available)
// 2. Initialize the Strudel REPL context
// 3. Replace the eval() call with proper Strudel pattern evaluation
// 4. Set up audio output through Web Audio API
Loading