PatchPad は、 WebAudio で動く ブラウザ モジュラー シンセ プレイグラウンド。 4 波形 OSC + ローパス フィルター + ADSR + LFO + 6 プリセット + 14-key 仮想鍵盤。 ラボ 29本目、 ジェネレーター ジャンル 3本目 (HueDeck = 配色、 TypeForge = 立体タイポ に続く)。
#なぜこの形にしたか
直近 5本は (doodle-drop: シミュレーター / side-tax: 計算 / atlas-quest: 学習 / hue-deck: ジェネレーター / astro-cast: データ可視化)。 直近 5本に シミュレーター/計算/学習/ジェネレーター/データ可視化 が各1。 避けるのは 3 本続いたときだけで、 ジェネレーター が hue-deck の 4本前のため再選択 OK。
候補比較:
- ブラウザ シンセ (採用)
- SVG モノグラム ジェネレーター (wow 薄い)
- 暗算 / 漢字 学習 3本目 (SEO 強だが tech wow 薄い)
- 動画 GIF レコーダー (doodle-drop の派生に見える)
選んだ理由:
- wow 強い: 純ブラウザでリアルタイム フィルター + LFO 変調 + ポリフォニー = 「これの?」 と言わせる王道
- WebAudio は ToneQuest で 1 回踏んでいる ので、 ノード接続 / 自動化 / Analyzer の知見が再利用できる
- SNS / 教育文脈: 「シンセ 入門」 「ADSR とは」 系コンテンツ需要が継続
- 思想: HueDeck (色) / TypeForge (字) と並ぶ 「ジェネレーター 3 兄弟」 で 「色 / 字 / 音」 が揃う
#見た目をどう決めたか
直近 5本の 見た目 を書き出す:
side-tax — ledger green + cream + stamp red (帳簿事務所)
atlas-quest — dark navy + amber departure (空港)
hue-deck — warm gray + ink + lipstick (Pantone)
astro-cast — midnight + 惑星 (天文台)
doodle-drop — blackboard green + chalk pastels (学校黒板)題材 「シンセ / 機材」 → モチーフ 候補:
- 70s モジュラー シンセ (Moog/ARP 風 walnut + brass) ← 採用
- 80s シンセウェーブ (neon + magenta + cyan grid) — astro-cast の rainbow に近い
- アナログ ミキサー (silver knobs) — neutral 過ぎ
- ピアノ + クラシック (黒漆 + 真鍮) — 単音楽器寄り
採用 3 要素:
- 配色: walnut wood
#3d2914+ cream panel#ebd9a7+ brass#b78a36+ chrome knob#d8d3c3+ cable orange#e8632a/ teal#1a8a8a/ amber#f1c873+ LED readout (black + 蛍光オレンジ) - 直近 5本どれとも 配色 別。 walnut wood は唯一。
- モチーフ: 70s モジュラー シンセ前面パネル — 木枠 + cream パネル + brass jacks + chrome knobs + LCD-like readout + 5 module sections (OSC / FILTER / ENV / LFO / OUT) + ホワイト鍵盤
- typography: Space Grotesk 700/800 (機材ラベルの工業的見栄え) + JetBrains Mono (数値 readout + module label / ノブ値)
#技術スタック
#Signal chain: OSC → Filter → VCA → Master → Analyser
const ctx = new AudioContext
const master = ctx.createGain
const analyser = ctx.createAnalyser
analyser.fftSize = 2048
master.connect(analyser)
analyser.connect(ctx.destination)
// LFO は filter.frequency を AudioParam 変調
const lfo = ctx.createOscillator
const lfoDepth = ctx.createGain
lfo.connect(lfoDepth)
lfo.startポイントは lfoDepth.connect(filter.frequency) の AudioParam 変調。 LFO の振幅 (gain) を 0〜3000 (Hz) に スケール して、 cutoff を周期的に揺らす。 これでアシッド / ワブル の音作りが成立。
#ADSR エンベロープ
function noteOn(midi: number) {
const now = ctx.currentTime
const vca = ctx.createGain
vca.gain.setValueAtTime(0, now)
vca.gain.linearRampToValueAtTime(0.6, now + params.envAttack)
vca.gain.linearRampToValueAtTime(
0.6 * params.envSustain,
now + params.envAttack + params.envDecay,
)
// release は noteOff で
}
function noteOff(v: Voice) {
const now = ctx.currentTime
const currentGain = v.vca.gain.value
v.vca.gain.cancelScheduledValues(now)
v.vca.gain.setValueAtTime(currentGain, now)
v.vca.gain.linearRampToValueAtTime(0, now + params.envRelease)
v.osc.stop(now + params.envRelease + 0.05)
}AudioParam の linearRampToValueAtTime を使えば サンプル正確なエンベロープが scheduling できる。 Note-off 時は cancelScheduledValues で進行中の curve をキャンセル → 現在値から release。
#ノブの drag rotate
function onPointerMove(e: PointerEvent) {
if (!dragRef.current) return
const dy = e.clientY - dragRef.current.startY
const next = fromDelta(dragRef.current.startVal, dy)
onChange(next)
}
// -135deg ~ 135deg、 240px ドラッグで full range
const toAngle = (v: number) => -135 + ((v - min) / (max - min)) * 270Pointer Events + setPointerCapture で マウスがノブ外に出ても継続。 cutoff など log scale が欲しいパラメータは log: true フラグで Math.log 補間。
#QWERTY 鍵盤入力
const KEYBOARD = [
{ midi: 60, label: "C4", black: false, qwerty: "a" },
{ midi: 61, label: "C#", black: true, qwerty: "w" },
// ...
]
window.addEventListener("keydown", (e) => {
if (e.repeat) return
const k = KEYBOARD.find((kk) => kk.qwerty === e.key.toLowerCase)
if (!k) return
engine.noteOn(k.midi)
})e.repeat で長押しの再発火を抑止 (= note-retrigger を防ぐ)。 piano roll に倣って 白鍵 = A S D F G H J K、 黒鍵 = W E T Y U。
#Polyphony
activeVoices: Map<midi, Voice> で 同時押し対応。 Note-off 時は該当 Voice の VCA を release → 一定時間後に OscillatorNode.stop + disconnect。 同 midi の再発音は既存 Voice を即時 release してから新規生成 (= mono-retrig)。
#まだやっていないこと
- WebMIDI 対応 (
navigator.requestMIDIAccess+ MIDIInput → noteOn/noteOff) - WAV / WebM 録音 (MediaRecorder で master 出力を録音 → ダウンロード)
- ステップ シーケンサー (16-step grid + tempo + 拍子)
- エフェクト チェーン (ConvolverNode リバーブ / DelayNode ディレイ / WaveShaper ディストーション)
- オシレーター 2 個目 (mix knob + 2nd OSC でデチューン感)
- 共有 URL で パッチ を base64 シリアライズ (preset 共有)
- タッチ 鍵盤の オクターブシフト (鍵盤を上下にスワイプ)
- AudioWorklet 化 (現状は標準ノードのみ、 カスタム DSP は未使用)
- Sub Bass / Acid Lead 系 50+ プリセット ライブラリ
[ ./next_action ]
読んだら、 PatchPad を実際に動かす。
この開発ログは PatchPad をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。