Skip to content
Open
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
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
30 changes: 29 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,29 @@
.DS_Store
.DS_Store

# dependencies
/node_modules
/.pnp
.pnp.js

# next.js
/.next/
/out/

# production
/build

# misc
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# typescript
*.tsbuildinfo
next-env.d.ts

# env
.env*.local
.env
145 changes: 142 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,143 @@
## About Me
# 🎛️ Beatsmith — MPC × Elektron Hip-Hop Production Copilot

* This website is a static HTML website using semantic tags and minimal CSS.
* It is currently hosted on Github Pages.
A **local-first web app** that guides you through the complete hip-hop production
process on a hardware-centered setup:

- **Akai MPC Live III** — master clock, main sequencer, arranger, sampler & song hub
- **Elektron Analog Rytm MKII** — drums, percussion, fills, parameter locks, analog drum design
- **Elektron Analog Four MKII** — bass, chords, leads, pads, textures & analog melodic synthesis

It is **not** a generic note-taking app. It's a structured production assistant built around the
MPC-as-master-clock-and-arranger workflow, continuously answering _"what should I create next, on
which device, and what creative options could work?"_

---

## ✨ What it does

- **15-stage guided workflow** — Project Setup → Tempo/Groove → Reference → Drums → Bass → Harmony
→ Hooks → Texture → Effects → Arrangement → Performance Automation → Transitions → Mix Prep →
Export → Final Review. Each stage gives you an objective, **per-device guidance** (MPC / Rytm / A4),
creative option cards, an idea generator, a checklist, notes, and a "mark complete" control.
- **11 hip-hop substyles** with BPM ranges, groove feel, drum/bass/harmony characteristics, effects
& arrangement tendencies, and device-specific suggestions (boom bap, trap, lo-fi, experimental,
dark cinematic, west coast, east coast, southern bounce, grimy underground, soul sample-based,
minimalist drum-machine).
- **Rule-based creative idea generator** — context-aware ideas based on substyle, stage, BPM, mood,
active devices, and what you've already saved. Hit **Generate more** for endless variations.
- **166 seed creative options** (15+ per category) across drums, bass, chords, melody, samples,
textures, effects, transitions, arrangement, performance automation, and mix prep — each with
recommended device, difficulty, style fit, when-to-use, implementation steps, and variations.
- **Arrangement builder** — a visual energy-coded timeline plus per-section editors for bars, active
devices, drum/bass/melody activity, energy, and notes on mutes/fills/drops/automation.
- **Hardware workflow planner** — editable device roles, MIDI channels, clock roles, audio routing,
and pattern-to-song / performance-capture strategies. Everything is an **editable assumption**.
- **Song completion checklist** + **local persistence** + **JSON backup export/import**.

---

## 🧱 Tech stack

| Layer | Choice |
| ------------ | ---------------------------------------- |
| Framework | Next.js 14 (App Router) |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS (dark-mode-first) |
| State / data | React Context + `localStorage` (no auth, no backend) |

---

## 🚀 Getting started

```bash
npm install
npm run dev # http://localhost:3000
```

Other scripts:

```bash
npm run build # production build (also type-checks)
npm run start # serve the production build
npm run lint # eslint
```

> Requires Node 18+ (developed on Node 22).

---

## 🗺️ Architecture overview

```
app/ # Next.js App Router pages (all client components)
page.tsx # Home / dashboard
projects/ # list · new · [id] overview
projects/[id]/workflow # the 15-stage guided workflow (centerpiece)
projects/[id]/arrangement # timeline + section editors
projects/[id]/ideas # idea generator + full creative library
projects/[id]/hardware # editable routing/clock/MIDI planner
settings/ # data export/import, default rig reference

components/ # ProjectCard, StageStepper, ProductionStagePanel,
# DeviceGuidanceCard, CreativeOptionCard, IdeaGeneratorPanel,
# Checklist, ArrangementTimeline, ArrangementSectionEditor,
# HardwareRoutingPlanner, SongCompletionChecklist, NotesPanel,
# SubstyleSelector, ProjectSettingsForm, ProjectNav, ProjectGate, ui

lib/
types.ts # strong domain types (Project, Device, ProductionStage,
# CreativeOption, ArrangementSection, HipHopSubstyle,
# ChecklistItem, HardwareRoutingProfile, …)
substyles.ts # 11 substyle definitions
creativeOptions.ts # 166 seed creative options (15+ per category)
stages.ts # the 15 production stages + completion checklist
devices.ts # default (editable) device profiles + routing
ideaGenerator.ts # rule-based, context-aware idea scoring/generation
projectFactory.ts # creates a fully-seeded project
store.tsx # localStorage-backed React context (CRUD + cross-tab sync)
```

**Data flow:** `StoreProvider` (in `app/layout.tsx`) hydrates projects from `localStorage`, exposes
CRUD via `useStore()`, and re-persists on every change. Pages resolve a project through
`ProjectGate` (which handles the hydration window) and mutate it with `updateProject(id, updater)`.

**Persistence is intentionally swappable** — replacing the load/save functions in `lib/store.tsx`
with `fetch` calls or a SQLite-backed API route is the entire migration path to a server backend.

---

## 🔧 Design assumptions (all editable in-app)

These are **sensible defaults, not verified hardware specs.** The app never hard-codes hardware
behavior as fact — you can edit device capabilities, MIDI channels, clock roles, and routing per
project on the **Hardware** page.

- MPC Live III is the **master clock** and sends MIDI clock + start/stop.
- Rytm & A4 **receive** clock + transport (CLOCK/TRANSPORT receive on the Elektrons).
- MPC MIDI out → Rytm and A4 (default A4 on ch 1–4, Rytm on its Auto channel — verify your config).
- Rytm = drums; A4 = bass/melody; MPC = arranger + sampler + audio capture of the external gear.
- Default new-project BPM is the midpoint of the chosen substyle's range; swing uses the substyle
default.

If your routing differs (USB vs DIN, different channels, A4 as clock, etc.), just edit it — the
guidance text and planner are data, not assumptions baked into code.

---

## 🛣️ Future enhancements

- **WebMIDI** — send real MIDI clock/transport and program changes to the hardware from the browser.
- **MIDI clock diagnostics** — verify sync, jitter, and latency between MPC and the Elektrons.
- **AI-assisted generation** — swap the rule-based generator for an LLM that proposes full
patterns/progressions from the project context.
- **Sample library tagging** — catalog chops/one-shots/breaks with key/BPM/mood tags and surface
them inside relevant stages.
- **Export templates** — generate stem/track naming sheets and session recall docs automatically.
- **SQLite / server sync** — optional account-based sync by swapping the persistence layer.

---

## 📦 Notes

The repository's previous static "About Me" page (`index.html`, `css/`, `img/`) is left untouched;
the Next.js app lives alongside it and is the active project.
65 changes: 65 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
color-scheme: dark;
}

html,
body {
background-color: #070809;
color: #e7eaf0;
}

body {
font-feature-settings: "cv02", "cv03", "cv04", "cv11";
-webkit-font-smoothing: antialiased;
}

/* Thin custom scrollbar for the studio feel */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: #0c0e12;
}
::-webkit-scrollbar-thumb {
background: #272e3b;
border-radius: 999px;
border: 2px solid #0c0e12;
}
::-webkit-scrollbar-thumb:hover {
background: #3a4456;
}

@layer components {
.card {
@apply rounded-xl border border-ink-700 bg-ink-850 shadow-card;
}
.card-hover {
@apply transition hover:border-ink-600 hover:bg-ink-800;
}
.btn {
@apply inline-flex items-center justify-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/60 disabled:cursor-not-allowed disabled:opacity-50;
}
.btn-primary {
@apply btn bg-accent text-ink-950 hover:bg-accent-soft;
}
.btn-ghost {
@apply btn border border-ink-700 bg-ink-800 text-gray-200 hover:border-ink-600 hover:bg-ink-700;
}
.btn-subtle {
@apply btn text-gray-300 hover:bg-ink-800;
}
.input {
@apply w-full rounded-lg border border-ink-700 bg-ink-900 px-3 py-2 text-sm text-gray-100 placeholder:text-gray-500 focus:border-accent/60 focus:outline-none focus:ring-1 focus:ring-accent/40;
}
.label {
@apply mb-1.5 block text-xs font-medium uppercase tracking-wide text-gray-400;
}
.chip {
@apply inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-xs font-medium;
}
}
27 changes: 27 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Metadata } from "next";
import "./globals.css";
import { StoreProvider } from "@/lib/store";
import { Sidebar } from "@/components/Sidebar";

export const metadata: Metadata = {
title: "Beatsmith — MPC + Elektron Hip-Hop Copilot",
description:
"A guided hip-hop production assistant for the Akai MPC Live III, Elektron Analog Rytm MKII, and Analog Four MKII.",
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className="min-h-screen bg-ink-950 text-gray-100 antialiased">
<StoreProvider>
<div className="flex min-h-screen">
<Sidebar />
<main className="flex-1 min-w-0">
<div className="mx-auto w-full max-w-6xl px-5 py-8 md:px-8">{children}</div>
</main>
</div>
</StoreProvider>
</body>
</html>
);
}
105 changes: 105 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use client";

import Link from "next/link";
import { useStore } from "@/lib/store";
import { ProjectCard } from "@/components/ProjectCard";
import { SectionHeading } from "@/components/ui";

const DEVICES = [
{ tag: "MPC Live III", role: "Master clock · arranger · sampler", cls: "text-mpc border-mpc/40 bg-mpc/10" },
{ tag: "Analog Rytm MKII", role: "Drums · percussion · fills", cls: "text-rytm border-rytm/40 bg-rytm/10" },
{ tag: "Analog Four MKII", role: "Bass · chords · leads · texture", cls: "text-a4 border-a4/40 bg-a4/10" },
];

export default function HomePage() {
const { projects, hydrated } = useStore();
const recent = projects.slice(0, 3);

return (
<div className="space-y-10">
{/* Hero */}
<section className="card overflow-hidden p-0">
<div className="bg-gradient-to-br from-ink-850 to-ink-900 p-8">
<p className="mb-2 text-xs font-medium uppercase tracking-widest text-accent-soft">
Hip-hop production copilot
</p>
<h1 className="max-w-2xl text-3xl font-bold leading-tight text-gray-50">
From idea to arrangement to mix prep — guided for your MPC + Elektron rig.
</h1>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-gray-400">
A structured, 15-stage workflow that tells you what to build next, what each
device should do, and gives you endless context-aware creative options along the way.
</p>
<div className="mt-6 flex flex-wrap gap-3">
<Link href="/projects/new" className="btn-primary px-4 py-2.5 text-sm">
+ Start a new beat
</Link>
<Link href="/projects" className="btn-ghost px-4 py-2.5 text-sm">
View projects
</Link>
</div>

<div className="mt-8 grid gap-3 sm:grid-cols-3">
{DEVICES.map((d) => (
<div key={d.tag} className={`rounded-xl border p-3.5 ${d.cls}`}>
<div className="text-sm font-semibold">{d.tag}</div>
<div className="mt-0.5 text-xs text-gray-400">{d.role}</div>
</div>
))}
</div>
</div>
</section>

{/* Recent projects */}
<section>
<SectionHeading
title="Recent projects"
subtitle="Pick up where you left off"
right={
<Link href="/projects" className="btn-subtle px-2 py-1 text-xs">
All projects →
</Link>
}
/>
{!hydrated ? (
<div className="py-10 text-center text-sm text-gray-500">Loading…</div>
) : recent.length === 0 ? (
<div className="card flex flex-col items-center gap-3 px-6 py-12 text-center">
<div className="text-4xl">🎚️</div>
<h3 className="text-base font-semibold text-gray-200">No projects yet</h3>
<p className="max-w-md text-sm text-gray-400">
Create your first production and the copilot will guide you stage by stage.
</p>
<Link href="/projects/new" className="btn-primary mt-1 px-4 py-2 text-sm">
+ Start a new beat
</Link>
</div>
) : (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{recent.map((p) => (
<ProjectCard key={p.id} project={p} />
))}
</div>
)}
</section>

{/* How it works */}
<section>
<SectionHeading title="How it works" subtitle="A complete production, broken into clear moves" />
<div className="grid gap-4 sm:grid-cols-3">
{[
{ n: "01", t: "Set the target", b: "Pick a substyle, BPM, key, and mood. The app seeds smart defaults for your rig." },
{ n: "02", t: "Work the stages", b: "15 guided stages from drums to mix prep — each with per-device actions and idea cards." },
{ n: "03", t: "Arrange & finish", b: "Build sections on the timeline, capture performances, and run the completion checklist." },
].map((s) => (
<div key={s.n} className="card p-5">
<div className="mb-2 font-mono text-xs text-accent-soft">{s.n}</div>
<h3 className="mb-1 text-sm font-semibold text-gray-100">{s.t}</h3>
<p className="text-sm leading-relaxed text-gray-400">{s.b}</p>
</div>
))}
</div>
</section>
</div>
);
}
Loading