


Building ColDAW from 0 to 1
By Joe Deng | Mar 2026
What is ColDAW
ColDAW is a collaborative music production platform that helps music creators and teams keep project versions organized and collaborate across tools, so they spend less time managing files and more time creating.
How It Works Technically
ColDAW parses Ableton .als files into a tool-agnostic schema, then layers Git-style versioning, structural diffs, branching, and merging on top.

Components:
a React 18 + TypeScript web app
a C++/JUCE VST3 plugin
a Node.js + PostgreSQL + Redis backend
Part I - Ideation
The Problem
Music creators waste hours on organizing files like final_v2_REAL_final.als.




Insights
Shouldn't ask users to leave their workstation: a web interface + a plugin
The tooling framework as a product: build an API instead of an app
Cross ecosystem integration: support most major workstations
Solution

Part II - R&D from 0 to 1
Tech Stack

Layer | Tech |
|---|---|
Plugin | C++ / JUCE / VST3 |
Backend | Node.js, Express, TypeScript, Socket.io |
Database | PostgreSQL + Redis |
Frontend | React 18, TypeScript, Vite, Zustand, styled-components |
File storage | Alibaba Cloud OSS |
Deployment | Railway, Docker |
Decision Walk-Through
Parser
.als is a gzipped XML file with an undocumented schema that drifts across Ableton versions. If this fails, nothing else could work.
// services/abletonParser.ts export const parseAbletonProject = async (filePath: string) => { const compressed = await fs.readFile(filePath); const xml = gunzipSync(compressed).toString('utf-8'); const parsed = await new xml2js.Parser().parseStringPromise(xml); const liveset = parsed.Ableton.LiveSet[0]; return { tempo: extractTempo(liveset), timeSignature: extractTimeSignature(liveset), tracks: extractTracks(liveset), automation: extractAutomation(liveset), }; };
// services/abletonParser.ts export const parseAbletonProject = async (filePath: string) => { const compressed = await fs.readFile(filePath); const xml = gunzipSync(compressed).toString('utf-8'); const parsed = await new xml2js.Parser().parseStringPromise(xml); const liveset = parsed.Ableton.LiveSet[0]; return { tempo: extractTempo(liveset), timeSignature: extractTimeSignature(liveset), tracks: extractTracks(liveset), automation: extractAutomation(liveset), }; };
Five categories we learned from how producers actually describe changes:
Category | Contents |
|---|---|
Track structure | Ordering, type, grouping |
Clip contents | Start, length, MIDI notes, sample refs |
Automation | Parameter-keyed envelopes |
Global parameters | Tempo, time sig, loop region |
Device & routing | Inserts, sends, signal flow |
Versioning
Design Decision
Auto-apply non-conflicting changes.

Development Decision
A versioning API with the same shape as Git:
Commit { id, projectId, message, parentCommitId, snapshotPath, timestamp } Branch { id, name, projectId, headCommitId }
Commit { id, projectId, message, parentCommitId, snapshotPath, timestamp } Branch { id, name, projectId, headCommitId }
A snapshot is the serialized symbolic representation, stored on disk, referenced by the commit. Merge walks both snapshots' track trees:
const diffSnapshots = (a: Snapshot, b: Snapshot) => { const changes = []; for (const track of b.tracks) { const prev = a.tracks.find(t => t.id === track.id); if (!prev) changes.push({ type: 'added', track }); else if (track.muted !== prev.muted) changes.push({ type: 'modified', field: 'muted', track }); // clips, automation, devices... } return changes; };
const diffSnapshots = (a: Snapshot, b: Snapshot) => { const changes = []; for (const track of b.tracks) { const prev = a.tracks.find(t => t.id === track.id); if (!prev) changes.push({ type: 'added', track }); else if (track.muted !== prev.muted) changes.push({ type: 'modified', field: 'muted', track }); // clips, automation, devices... } return changes; };

VST3 Plugin
Design Decision
One-click upoad and in-plugin audio assets inspector, allowing for upload directly within local workstation without having to open website.
Development Decision
Built in JUCE/C++. Loads as a normal audio plugin, authenticates with the backend, detects the open project, hashes referenced samples, dedupes against the server, and uploads only the diff.
void uploadProjectSamples(const String& projectId, const String& alsPath) { auto audioFiles = collectReferencedAudioFiles(alsPath); std::vector<FileInfo> infos; for (auto& file : audioFiles) infos.push_back({ file, calculateSHA256(file) }); auto existing = checkFilesExist(projectId, extractHashes(infos)); for (auto& info : infos) if (!existing.contains(info.hash)) uploadAudioFile(projectId, info.filePath); }
void uploadProjectSamples(const String& projectId, const String& alsPath) { auto audioFiles = collectReferencedAudioFiles(alsPath); std::vector<FileInfo> infos; for (auto& file : audioFiles) infos.push_back({ file, calculateSHA256(file) }); auto existing = checkFilesExist(projectId, extractHashes(infos)); for (auto& info : infos) if (!existing.contains(info.hash)) uploadAudioFile(projectId, info.filePath); }
Web App UI
Design decision
Editor: An interface feels similar to music producers, muti-color color pallete, and actual clip waveform and midi pitch and vvelocity redering, refering time-based media editing softwares.
Inspector & Sidebar view: gives users a similar UXUI feel of figma and real DAW interface. Allows for inspecting parameters and exporting per-track formats.

Development Decision
React 18 + TypeScript + Vite + Zustand for state, Socket.io for real-time.
The arrangement view renders per-clip change highlighting, color-coded against the previous commit.
<MainContent style={{ marginRight: inspectorOpen ? 320 : audioSidebarOpen ? 240 : 0 }}>
<MainContent style={{ marginRight: inspectorOpen ? 320 : audioSidebarOpen ? 240 : 0 }}>
Collaboration
Design decision
Last-write-wins on parameter edits with optimistic client rendering. Edits appear instantly.
Development Decision
Socket.io
socket.emit('join-project', { projectId, userName, userId }); socket.on('project-update', (changes) => applyChanges(changes)); socket.on('user-joined', ({ collaborator }) => addCollaborator(collaborator)); socket.on('cursor-update', ({ socketId, x, y }) => updateCursor(socketId, x, y));
socket.emit('join-project', { projectId, userName, userId }); socket.on('project-update', (changes) => applyChanges(changes)); socket.on('user-joined', ({ collaborator }) => addCollaborator(collaborator)); socket.on('cursor-update', ({ socketId, x, y }) => updateCursor(socketId, x, y));
Cross-tool Translation
Example: TouchDesigner
// server GET /api/projects/:id/touchdesigner → { tempo, tracks: [{ name, volume, clips: [...] }], automation }
// server GET /api/projects/:id/touchdesigner → { tempo, tracks: [{ name, volume, clips: [...] }], automation }
# TouchDesigner client def onFrameStart(frame): if frame % 30 == 0: r = requests.get(f'{API}/projects/{ID}/touchdesigner', headers=AUTH) op('tempo').par.value0 = r.json()['tempo']
# TouchDesigner client def onFrameStart(frame): if frame % 30 == 0: r = requests.get(f'{API}/projects/{ID}/touchdesigner', headers=AUTH) op('tempo').par.value0 = r.json()['tempo']
Storage
First deploy lost user data on every push (Railway containers are ephemeral). Fix was a DATA_DIR env var so the same code runs against local dev, Railway volumes, or S3.
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, '..', 'data'); const uploadDir = path.join(DATA_DIR, 'uploads'); const projectsDir = path.join(DATA_DIR, 'projects');
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, '..', 'data'); const uploadDir = path.join(DATA_DIR, 'uploads'); const projectsDir = path.join(DATA_DIR, 'projects');
Pipeline

Audio samples → Alibaba Cloud OSS.
Project metadata → PostgreSQL.
Sessions → Redis with TTLs
Validation

Team and Operating
8-person team across NYU Tisch and Georgia Tech Music Tech and CS. Weekly sprints in Lark, GitHub, Figma. I run product strategy and hold the founder seat across all three groups (engineering, design, GTM).

Building ColDAW from 0 to 1
By Joe Deng | Mar 2026
What is ColDAW
ColDAW is a collaborative music production platform that helps music creators and teams keep project versions organized and collaborate across tools, so they spend less time managing files and more time creating.
How It Works Technically
ColDAW parses Ableton .als files into a tool-agnostic schema, then layers Git-style versioning, structural diffs, branching, and merging on top.

Components:
a React 18 + TypeScript web app
a C++/JUCE VST3 plugin
a Node.js + PostgreSQL + Redis backend
Part I - Ideation
The Problem
Music creators waste hours on organizing files like final_v2_REAL_final.als.




Insights
Shouldn't ask users to leave their workstation: a web interface + a plugin
The tooling framework as a product: build an API instead of an app
Cross ecosystem integration: support most major workstations
Solution

Part II - R&D from 0 to 1
Tech Stack

Layer | Tech |
|---|---|
Plugin | C++ / JUCE / VST3 |
Backend | Node.js, Express, TypeScript, Socket.io |
Database | PostgreSQL + Redis |
Frontend | React 18, TypeScript, Vite, Zustand, styled-components |
File storage | Alibaba Cloud OSS |
Deployment | Railway, Docker |
Decision Walk-Through
Parser
.als is a gzipped XML file with an undocumented schema that drifts across Ableton versions. If this fails, nothing else could work.
// services/abletonParser.ts export const parseAbletonProject = async (filePath: string) => { const compressed = await fs.readFile(filePath); const xml = gunzipSync(compressed).toString('utf-8'); const parsed = await new xml2js.Parser().parseStringPromise(xml); const liveset = parsed.Ableton.LiveSet[0]; return { tempo: extractTempo(liveset), timeSignature: extractTimeSignature(liveset), tracks: extractTracks(liveset), automation: extractAutomation(liveset), }; };
Five categories we learned from how producers actually describe changes:
Category | Contents |
|---|---|
Track structure | Ordering, type, grouping |
Clip contents | Start, length, MIDI notes, sample refs |
Automation | Parameter-keyed envelopes |
Global parameters | Tempo, time sig, loop region |
Device & routing | Inserts, sends, signal flow |
Versioning
Design Decision
Auto-apply non-conflicting changes.

Development Decision
A versioning API with the same shape as Git:
Commit { id, projectId, message, parentCommitId, snapshotPath, timestamp } Branch { id, name, projectId, headCommitId }
A snapshot is the serialized symbolic representation, stored on disk, referenced by the commit. Merge walks both snapshots' track trees:
const diffSnapshots = (a: Snapshot, b: Snapshot) => { const changes = []; for (const track of b.tracks) { const prev = a.tracks.find(t => t.id === track.id); if (!prev) changes.push({ type: 'added', track }); else if (track.muted !== prev.muted) changes.push({ type: 'modified', field: 'muted', track }); // clips, automation, devices... } return changes; };

VST3 Plugin
Design Decision
One-click upoad and in-plugin audio assets inspector, allowing for upload directly within local workstation without having to open website.
Development Decision
Built in JUCE/C++. Loads as a normal audio plugin, authenticates with the backend, detects the open project, hashes referenced samples, dedupes against the server, and uploads only the diff.
void uploadProjectSamples(const String& projectId, const String& alsPath) { auto audioFiles = collectReferencedAudioFiles(alsPath); std::vector<FileInfo> infos; for (auto& file : audioFiles) infos.push_back({ file, calculateSHA256(file) }); auto existing = checkFilesExist(projectId, extractHashes(infos)); for (auto& info : infos) if (!existing.contains(info.hash)) uploadAudioFile(projectId, info.filePath); }
Web App UI
Design decision
Editor: An interface feels similar to music producers, muti-color color pallete, and actual clip waveform and midi pitch and vvelocity redering, refering time-based media editing softwares.
Inspector & Sidebar view: gives users a similar UXUI feel of figma and real DAW interface. Allows for inspecting parameters and exporting per-track formats.

Development Decision
React 18 + TypeScript + Vite + Zustand for state, Socket.io for real-time.
The arrangement view renders per-clip change highlighting, color-coded against the previous commit.
<MainContent style={{ marginRight: inspectorOpen ? 320 : audioSidebarOpen ? 240 : 0 }}>
Collaboration
Design decision
Last-write-wins on parameter edits with optimistic client rendering. Edits appear instantly.
Development Decision
Socket.io
socket.emit('join-project', { projectId, userName, userId }); socket.on('project-update', (changes) => applyChanges(changes)); socket.on('user-joined', ({ collaborator }) => addCollaborator(collaborator)); socket.on('cursor-update', ({ socketId, x, y }) => updateCursor(socketId, x, y));
Cross-tool Translation
Example: TouchDesigner
// server GET /api/projects/:id/touchdesigner → { tempo, tracks: [{ name, volume, clips: [...] }], automation }
# TouchDesigner client def onFrameStart(frame): if frame % 30 == 0: r = requests.get(f'{API}/projects/{ID}/touchdesigner', headers=AUTH) op('tempo').par.value0 = r.json()['tempo']
Storage
First deploy lost user data on every push (Railway containers are ephemeral). Fix was a DATA_DIR env var so the same code runs against local dev, Railway volumes, or S3.
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, '..', 'data'); const uploadDir = path.join(DATA_DIR, 'uploads'); const projectsDir = path.join(DATA_DIR, 'projects');
Pipeline

Audio samples → Alibaba Cloud OSS.
Project metadata → PostgreSQL.
Sessions → Redis with TTLs
Validation

Team and Operating
8-person team across NYU Tisch and Georgia Tech Music Tech and CS. Weekly sprints in Lark, GitHub, Figma. I run product strategy and hold the founder seat across all three groups (engineering, design, GTM).

ColDAW Deck
ColDAW is a digital studio that makes it easy for music creatives and multimedia collaborators to share, manage, and collaborate on music projects across different software. ColDAW lets collaborators work in parallel across various audio workstations and multimedia software, and automatically keeps everything in sync.
Year
2025
Year
2025
Role
Founder
Role
Founder
Client
This is a self-lead project.
Client
This is a self-lead project.
Timeline
5 month
Timeline
5 month
ColDAW Deck
ColDAW is a digital studio that makes it easy for music creatives and multimedia collaborators to share, manage, and collaborate on music projects across different software. ColDAW lets collaborators work in parallel across various audio workstations and multimedia software, and automatically keeps everything in sync.
Year
2025
Role
Founder
Client
This is a self-lead project.
Timeline
5 month
