BITCODE は、初代 Game Boy(DMG-01)のサウンドチップ LR35902 の4チャンネル構成を制約として積極的に活用する、ブラウザベースのチップチューン・ライブコーディング言語である。
Strudel が TidalCycles のパターン言語を Web に持ち込んだように、BITCODE は DMG音源の制約そのものをパターン言語として再定義 する。自由度の高い汎用シンセシスではなく、4bit音量・矩形波のデューティ比・32サンプル波形メモリ・LFSRノイズという限られたパラメータ空間のなかでパターンを操作することが、この言語の美学的・設計的なアイデンティティとなる。
UIは完全な1bitモノクロ(#fff / #000 の2色のみ)で構成され、視覚面でも「制約の美学」を体現する。
- 共有するもの: サイクルベースのパターン・モデル、ミニノーテーション風の簡潔な記法、メソッドチェーンによる変換の合成、ブラウザ完結
- 異なるもの: 音源が Web Audio API の汎用ノードではなく DMG の4チャンネルに固定される。チャンネルの物理的制約(同時に1音しか鳴らないモノフォニック構造)がパターン設計に影響する。パラメータ空間が離散的・有限(4bitの16段階音量、4種のデューティ比 etc.)
┌─────────────────────────────────────────┐
│ BITCODE Engine │
│ │
│ p1 ──▶ [CH1: Pulse+Sweep] ──┐ │
│ p2 ──▶ [CH2: Pulse ] ──┤ │
│ p3 ──▶ [CH3: Wave Memory] ──┼──▶ 🔊 │
│ n1 ──▶ [CH4: Noise ] ──┘ │
│ │
│ Each channel: 1 persistent oscillator │
│ New event→ immediately overwrites prev │
│ Master gain: 2.5 │
└─────────────────────────────────────────┘
4つの関数が4つのチャンネルに1対1で対応する。各チャンネルは固定の発音体を1つだけ保持し、新しいイベントが到達すると前の音(リリース途中であっても)を強制的に上書きする。これは DMG 実機と同じ動作であり、「4チャンネルをどう使い分けるか」という作曲的思考を強制する設計上の意図的な制約である。
// CH1: 矩形波でメロディ
p1("c4 e4 g4 c5")
// CH2: 矩形波でハーモニー / 対旋律
p2("c3 ~ e3 ~")
// CH3: 波形メモリで低音
p3("c2 c2 g2 g2")
// CH4: ノイズでリズム
n1("h l ~ h") // h=high(短周期/7bit), l=low(長周期/15bit)TidalCycles / Strudel のミニノーテーション構文を採用する:
| 記法 | 意味 |
|---|---|
| スペース区切り | サイクル内の等分割 |
~ |
休符 |
[ ] |
サブディビジョン(時間の細分化) |
< > |
オルタネート(サイクルごとに交替) |
*n |
繰り返し |
? |
確率的発音(50%の確率) |
// サブディビジョン:16分音符的な動き
p1("c4 [e4 g4] a4 [g4 e4]")
// オルタネート:サイクルごとに交替
p1("<c4 e4 g4 c5> <e4 g4 b4 e5>")
// 繰り返し
n1("h*4 l*2 h l")
// 確率的発音
n1("h l? h ~") // l が50%の確率で発音p1("c4 e4 g4 c5").duty(50) // 全ノート一律 50%
p1("c4 e4 g4 c5").duty("12 25 50 75") // 音ごとにパターン化
// 値: 12(=12.5%), 25, 50, 75p1("c4 e4 g4 c5").vol(8) // 一律 (0-15)
p1("c4 e4 g4 c5").vol("15 12 8 4") // パターン化DMG のエンベロープ: 初期音量(0-15)、方向(up/down)、速度(1-7)
p1("c4 e4 g4 c5").env(15, "down", 3)
// 音量15から減衰、速度3// プリセット波形
p3("c3 e3 g3 c4").wave("tri") // 三角波
p3("c3 e3 g3 c4").wave("saw") // 鋸歯波
p3("c3 e3 g3 c4").wave("sin") // 正弦波風
p3("c3 e3 g3 c4").wave("sqr") // 矩形波
// 波形を16進数で直接記述(32文字 = 32サンプル × 4bit)
p3("c3").wave("0123456789abcdeffedcba9876543210")
// 波形のパターン化(サイクルごとに交替)
p3("c3 e3 g3 c4").wave("<tri saw sin>")n1("h l h l") // h=短周期(7bit), l=長周期(15bit)
n1("h l h l").env(15, "down", 1) // エンベロープ付き| 変換 | 構文 | 説明 |
|---|---|---|
| 倍速 | .fast(n) |
パターンをn倍速で繰り返す |
| 減速 | .slow(n) |
パターンをn倍遅く |
| 逆行 | .rev() |
パターンを逆順再生 |
| 周期的変換 | .every(n, fn) |
nサイクルごとにfnを適用 |
| 確率的変換 | .sometimes(fn) |
50%の確率でfnを適用 |
| 高確率変換 | .often(fn) |
75%の確率でfnを適用 |
| 低確率変換 | .rarely(fn) |
25%の確率でfnを適用 |
| 回文 | .palindrome() |
偶数サイクルで逆行 |
| 回転 | .rotate(n) |
パターンをn要素分ずらす |
p1("c4 e4 g4 c5").fast(2)
p1("c4 e4 g4 c5").every(4, x => x.fast(2))
p1("c4 e4 g4 c5").sometimes(x => x.duty(12))
p1("c4 e4 g4 c5").palindrome()
p1("c4 e4 g4 c5").rotate(2)
// 変換の合成:4サイクルごとに逆行の倍速
p1("c4 e4 g4 c5").every(4, x => x.rev().fast(2))// アルペジオ:1ノートを高速にピッチ切替(半音間隔で指定)
p1("c4 e4").arp("0 4 7") // 長三和音アルペジオ
// スウィープ:周波数を連続的に変化(CH1推奨)
p1("c4 ~ c4 ~").sweep(3, "up") // 上昇スウィープ、速度3
p1("c5 ~ c5 ~").sweep(3, "down") // 下降スウィープ
// エコー:音量減衰の擬似ディレイ
p1("c4 e4 g4 c5").echo(3, 0.25) // 3回反復、間隔0.25
// グリッサンド:次のノートへ離散的にスライド
p1("c4 ~ c5 ~").gliss(4) // 4ステップでスライドp1("c4 e4 g4 c5").solo() // このチャンネルのみ再生
p2("c3 e3 g3 c4").mute() // このチャンネルをミュートbpm(140) // テンポ設定
hush() // 全チャンネル停止┌──────────────────────────────────────────┐
│ BITCODE v0.3 ▶ PLAY ■ STOP BPM │ ← ヘッダー(固定)
├──────────────────────────────────────────┤
│ 1│ bpm(104) │
│ 2│ │
│ 3│ p1("~ a4 c5 ~ e5 ~ d5 c5 ~ a4 ~ g4") │
│ 4│ .duty("<50 25 50 25>") │ ← CodeMirror エディタ
│ 5│ .env(11, "down", 4) │ 行番号・ハイライト付き
│ 6│ .rotate(1) │
│ 7│ .every(4, x => x.rev()) │
│ │ │
│ │ Error message (inline display) │
├──────────────────────────────────────────┤
│ [CH1] [CH2] [CH3] [CH4] │ ← チャンネルモニタ
│ ------------ Waveform Scope ----------- │ ← AnalyserNode波形表示
│ p1 p2 pulse · p3 wave · n1 noise │ ... │ ← ヘルプ
└──────────────────────────────────────────┘
完全1bitモノクロ:
#ffffff— 背景#000000— テキスト、ボーダー、すべてのUI要素
中間色を一切使わない。この制約がBITCODEの視覚的アイデンティティとなる。
CodeMirror 5 を統合し、カスタム bitcode モードによるシンタックスハイライトを実装。1bitモノクロ制約の中で、太字・イタリック・透明度の3軸で構文要素を視覚的に分離する。
| 構文要素 | スタイル |
|---|---|
p1 p2 p3 n1 チャンネル名 |
太字 |
bpm hush グローバル関数 |
太字 |
.duty() .fast() 等メソッド |
イタリック |
// コメント |
薄い(opacity 35%) |
ミニノーテーション内ノート c4 e5 |
太字 |
ノイズトークン h l |
太字 |
休符 ~ |
薄い(opacity 30%) |
[ ] < > ( ) 括弧 |
太字 |
=> アロー |
太字 |
コールバック変数 x |
イタリック |
追加機能:
- 行番号表示
- 括弧の対応ハイライト(
matchBrackets) - 括弧の自動閉じ(
autoCloseBrackets) - アクティブ行のハイライト(
styleActiveLine)
エラーはエディタ最下行の直下に CodeMirror のインラインウィジェットとして表示する。メソッド名のスペルミスに対しては、レーベンシュタイン距離による「did you mean ...?」サジェストを提供する。
.evry(4, x => x.fast(2))
✕ x.evry is not a function — did you mean .every()?
各チャンネルのアクティブ状態をモノクロ反転で表示する。.solo() 時は「S」、.mute() 時は「M」を表示。
AnalyserNode に接続されたリアルタイム波形スコープ。HiDPI(devicePixelRatio)対応。
- Ctrl+Enter (Mac: ⌘+Enter): コードを評価し再生開始 / パターン更新
- ▶ PLAY / ⟳ EVAL: ボタンによる評価
- ■ STOP: 全停止
┌───────────────────────────────────────────┐
│ Browser │
│ │
│ ┌───────────┐ ┌──────────────────┐ │
│ │ CodeMirror│──▶│ Pattern Engine │ │
│ │ Editor │ │ │ │
│ │ │ │ - Mini-notation │ │
│ │ bitcode │ │ Tokenizer │ │
│ │ mode + │ │ - Pattern class │ │
│ │ 1bit theme│ │ (query model) │ │
│ └───────────┘ │ - Transforms │ │
│ └───────┬──────────┘ │
│ │ events │
│ ┌───────▼──────────┐ │
│ │ Scheduler │ │
│ │ 150ms lookahead │ │
│ │ 50ms interval │ │
│ └───────┬──────────┘ │
│ │ noteOn/Off │
│ ┌───────▼──────────┐ │
│ │ GB Sound Engine │ │
│ │ (Persistent │ │
│ │ Oscillators) │ │
│ │ │ │
│ │ CH1: OscNode │ │
│ │ + PeriodicWave│ │
│ │ CH2: OscNode │ │
│ │ + PeriodicWave│ │
│ │ CH3: OscNode │ │
│ │ + PeriodicWave│ │
│ │ CH4: BufSrc × 2 │ │
│ │ + LFSR xfade │ │
│ │ │ │
│ │ Master (2.5) ──▶ │ │
│ │ AnalyserNode ──▶│ │
│ │ Destination │ │
│ │ │ │
│ │ Web Audio API │ │
│ └──────────────────┘ │
└───────────────────────────────────────────┘
- CodeMirror 5.65.7 を CDN から読み込み
- カスタム
bitcodeモードで構文トークナイズ(文字列内のミニノーテーション構文も認識) - カスタム
bitcodeテーマ(完全1bitモノクロ、太字/イタリック/透明度による分離) - アドオン:
matchBrackets,autoCloseBrackets,styleActiveLine - エラー表示:
addLineWidgetによるインラインウィジェット - Ctrl+Enter / ⌘+Enter を
extraKeysで評価にバインド
- Pattern<T>: 時間区間 → イベント列 の関数。Strudel と同じ「時間を問い合わせるとイベントが返る」モデル
- MiniNotation Tokenizer: 再帰的トークナイザ。
[ ]でネスト、< >でオルタネート、*nで繰り返し、?で確率的発音 - パラメータ:
.duty()等で返される新しい Pattern インスタンスにパラメータが付与される(イミュータブルチェーン) - 変換:
.fast(),.rev(),.sometimes(),.palindrome(),.rotate()等は元の Pattern を包む新しい Pattern を返す(関数合成) - Proxy バインディング: チャンネルに紐づいた Pattern のメソッドチェーンが、自動的にスケジューラのチャンネルスロットを更新する
AudioContext.currentTimeを参照し、150ms 先読みでイベントを発行- 50ms 間隔の
setTimeoutループ - サイクル単位でパターンにクエリを投げ、イベントを Web Audio API の時間に変換
- パラメータ文字列パターン(duty, vol)をステップインデックスで解決
.solo()/.mute()フラグによるチャンネルフィルタリング
実機 DMG と同じく、各チャンネルに固定の発音体を1つだけ保持する。新しいイベントが到達すると、前の音のリリース途中であっても即座に上書きされる(モノフォニック強制切替)。ノードの生成・破棄が不要なため、効率的かつクリーンな停止が可能。
- CH1/CH2: 永続
OscillatorNode+GainNode(各1組)PeriodicWaveでデューティ比を合成(フーリエ係数、結果キャッシュ)- ノートオン:
frequency.setValueAtTime()でピッチ即時切替、gainで音量セット - ノートオフ:
gainをゼロにセット(前の音は強制的に上書きされる) - アルペジオ: 同一 Oscillator の
frequency.setValueAtTime()を35ms間隔で連続切替 - スウィープ: 周波数を段階的に変化、音量も同時に減衰
- エコー: 同一チャンネル上で音量減衰させた複数イベントをスケジュール
- グリッサンド: 2ノート間を離散ステップで周波数遷移
- CH3: 永続
OscillatorNode+GainNode- 32サンプル×4bit の波形データを DFT で
PeriodicWaveに変換 - 波形が変更された時のみ
setPeriodicWave()で更新 - プリセット4種(tri, saw, sin, sqr)+ 16進数直接記述 +
<>パターン化 - サイクル番号を受け取り、波形オルタネートを解決
- 32サンプル×4bit の波形データを DFT で
- CH4: 永続
AudioBufferSourceNode× 2(短周期7bit + 長周期15bit を常時ループ再生)GainNodeのクロスフェードで短周期/長周期を即時切替- 両ソースを同時に走らせ、不使用側はゲイン0で無音化
- エンベロープ:
GainNode.gain.setValueAtTime()で離散ステップ制御 - マスター:
GainNode(gain: 2.5)→AnalyserNode→AudioContext.destination - 停止処理:
cancelScheduledValues()+ 即時ゼロで全ボイスを無音化。ノード破棄不要
new Function()によるevalで例外をキャッチ"is not a function"エラーに対し、レーベンシュタイン距離2以内の既知メソッド名をサジェスト- エラーは CodeMirror の
addLineWidgetでエディタ末尾にインライン表示 - 次の評価時に前のウィジェットを自動クリア
- p1〜p3 のノートパターン + n1 のノイズパターン
- ミニノーテーション(スペース区切り、
[ ]、~) -
.duty(),.wave(),.vol(),.env()の基本パラメータ -
.fast(),.slow(),.rev(),.every()の基本変換 - パラメータの文字列パターン化(duty, vol)
- textarea エディタ + Ctrl+Enter 評価
- チャンネルモニタ + 波形スコープ
- 1bit モノクロ UI
- bpm() / hush() グローバル制御
-
< >オルタネート(ミニノーテーション内) -
*n繰り返し -
?確率的発音(50%) -
.sometimes()/.often()/.rarely()確率的変換 -
.palindrome()回文変換 -
.rotate(n)回転変換 -
.arp()アルペジオ(半音間隔指定、35ms切替) -
.sweep()周波数スウィープ(速度・方向指定) -
.echo()擬似ディレイ(反復回数・間隔指定) -
.gliss()グリッサンド(次ノートへの離散ステップ遷移) - 波形メモリの16進数直接記述(32文字)
- 波形のパターン化
.wave("<tri saw>") -
.solo()/.mute()チャンネル制御 - AnalyserNode 接続によるリアルタイム波形スコープ(HiDPI対応)
- サイクルカウンタ表示
- 永続オシレーター・アーキテクチャ(モノフォニック強制切替)
- CodeMirror 5 統合
- カスタム
bitcodeシンタックスハイライトモード - 1bitモノクロテーマ(太字/イタリック/透明度)
- 行番号表示
- 括弧の対応ハイライト(matchBrackets)
- 括弧の自動閉じ(autoCloseBrackets)
- アクティブ行ハイライト(styleActiveLine)
- エラーのインラインウィジェット表示
- スペルミスの「did you mean」サジェスト(レーベンシュタイン距離)
- パラメータ文字列(
.duty(),.vol())では< >オルタネート構文を使用不可。スペース区切りの数値列のみ対応 .gliss()は次のノートが同一パターン内に存在する場合のみ動作.sweep()は全チャンネルで動作するが、実機準拠では CH1 のみの機能- CH4 の
fast()高倍率(4以上)でsetValueAtTimeが密集しグリッチが発生する場合がある
- キーボードショートカット拡充
- モバイル対応
- URLエンコードによるシェア機能
- プリセット / サンプルコード集(UI内選択)
DMG の4チャンネル制限は、ライブコーディングにおいて「制約がクリエイティビティを駆動する」という原理を体現する。Strudel では無制限にチャンネルを追加できるが、BITCODE では物理的に4つしかない。この制約が:
- 選択の緊迫感: メロディに2チャンネル使うか、ベースに回すか
- チャンネル配分の実験: 同じ4chを異なるアレンジで再配分する
- 擬似ポリフォニーの技法:
.arp()による高速ピッチ切替で和音を表現する、チップチューンの伝統的技法の再発見
DMG のパラメータはすべて離散的:
- 音量: 16段階 (4bit)
- デューティ比: 4種類
- 波形: 32サンプル × 4bit
- ノイズ: 2モード × 8レート
この「粗い」パラメータ空間をパターン操作の対象にすることで、.duty("12 25 50 75") のように離散値を巡回させるデジタル特有のテクスチャが生まれる。
完全な白黒2色のUIは、単なる装飾的選択ではない。音源の制約(4bit, 4ch, 4 duty cycles)に呼応する視覚的制約であり、「制約のなかで表現する」というBITCODEの設計思想をインターフェースの層でも一貫させるものである。CodeMirror のシンタックスハイライトも、色を一切使わず太字・イタリック・透明度の3軸だけで構文を区別する設計は、この思想の延長にある。
チップチューン制作は従来トラッカー(LSDj, FamiTracker, MML等)で行われてきた。これらは本質的にシーケンスの直接配置——「結果としての音楽」を構成するアプローチ——だ。BITCODE はこれを TidalCycles 由来の「パターンの変換」というパラダイムに転換する。every(4, x => x.fast(2).rev()) のような記述は、音の配置ではなく変換規則を操作する。この認知的転換が、チップチューンの制作に新しい思考法をもたらす。
各チャンネルに固定の発音体を1つだけ保持し、新しいイベントが前の音を即座に上書きする。これは DMG 実機と同じ動作であり、以下の設計効果を持つ:
- リリースの断裁: エンベロープの減衰途中であっても新しいノートが容赦なく切り替わる。この「切れ方」がチップチューン特有のアーティキュレーションを生む
- チャンネルの物理性: 1チャンネル=1音という絶対的制約が、ノード生成ベースの実装では曖昧だった「同時にひとつしか鳴らない」原則を厳密に実現する
- パフォーマンス: ノードの生成・破棄がなく、
setValueAtTimeによるスケジューリングのみで動作するため、長時間のライブコーディングセッションでも安定する
Phase 2 で表現力の拡張(確率変換、アルペジオ、波形パターン化等)が完了し、Phase 3 で CodeMirror 統合・エラー処理が加わったことで、BITCODE は「実験的な音源エンジン」から「ライブコーディング環境」への転換を遂げた。シンタックスハイライトは単なる見た目の改善ではなく、コードの構造を即座に把握しリアルタイムで書き換えるライブコーディングの実践において不可欠な認知的支援である。
// === "The Sealed Forest" ===
bpm(104)
// CH1: A winding melody through the trees
// rotate makes the path shift each cycle
p1("~ a4 c5 ~ e5 ~ d5 c5 ~ a4 ~ g4")
.duty("<50 25 50 25>")
.env(11, "down", 4)
.rotate(1)
.every(4, x => x.rev())
.every(7, x => x.arp("0 3 7"))
// CH2: Distant bells, echoing through canopy
p2("~ ~ ~ e5 ~ ~ ~ ~ a5 ~ ~ ~")
.duty(12)
.vol(4)
.echo(2, 0.2)
.palindrome()
.every(5, x => x.rotate(3))
// CH3: Ancient roots — Am-Em-F-Dm
p3("a3 a3 a3 e3 e3 e3 f3 f3 f3 d3 d3 d3")
.wave("<tri tri saw tri>")
.vol(6)
.every(6, x => x.rev())
.every(8, x => x.rotate(3))
// CH4: Leaves rustling, footsteps on moss
n1("~ l? ~ ~ h ~ ~ l? ~ h? ~ ~")
.env(7, "down", 4)
.every(3, x => x.rotate(1))
.every(5, x => x.fast(2))| ライブラリ | バージョン | 用途 |
|---|---|---|
| React | 18.2.0 | UI描画 |
| ReactDOM | 18.2.0 | UI描画 |
| Babel Standalone | 7.23.9 | JSXトランスパイル |
| CodeMirror | 5.65.7 | コードエディタ |
| CodeMirror matchbrackets | 5.65.7 | 括弧対応表示 |
| CodeMirror closebrackets | 5.65.7 | 括弧自動閉じ |
| CodeMirror active-line | 5.65.7 | アクティブ行表示 |
すべて cdnjs.cloudflare.com から読み込み。インストール不要、ブラウザ完結。
BITCODE は DMG サウンドチップの動作原理を Web Audio API で再現するものであり、任天堂のファームウェア・BIOS・ROM を一切使用しない。矩形波・LFSRノイズ等の基本波形は自然法則に基づく生成であり、著作物には該当しない。チップに関連する特許は出願から20年以上経過し失効済み。半導体レイアウトの排他的権利も10年で消滅している。
「Game Boy」は任天堂の登録商標のため、製品名・マーケティングでの使用を避ける。「BITCODE」は汎用的な名称であり、商標上のリスクはきわめて低い。ドキュメント内での技術的言及には「DMG-01」「LR35902」等のチップ型番を使用する。