99LETTERS STUDIO · USER MANUAL

BLACK MIRROR BOARD — USAGE

VERSION v1.2 LANGUAGES 日本語 × English UPDATED 2026-04-19 STATUS ● LIVE
 ██╗   ██╗███████╗ █████╗  ██████╗ ███████╗
 ██║   ██║██╔════╝██╔══██╗██╔════╝ ██╔════╝
 ██║   ██║███████╗███████║██║  ███╗█████╗
 ██║   ██║╚════██║██╔══██║██║   ██║██╔══╝
 ╚██████╔╝███████║██║  ██║╚██████╔╝███████╗
  ╚═════╝ ╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚══════╝

§1Welcome to Black Mirror Board

ENBlack Mirror Board is an infinite canvas that amplifies a designer's intuition with an engineer's command layer.

🇯🇵 コードを書かない美学と、コマンドを打つ快感が交わる場所。それが Black Mirror です。

🇬🇧 It's where the aesthetic of "I don't write code" meets the thrill of "I speak to the machine."

Infinite canvas Interactive terminal Custom spells (localStorage) SNS-portable JSON 3 themes Zero deps

§2Basic Controls / 基本操作

Drawing / 描画

Action / 動作How / やり方
図形を描く / Draw a shapeツールバーから形を選び、キャンバスをドラッグ / Pick a tool, drag on canvas.
フリーハンド / FreehandP で Pen。始点近くでリリース → 自動クローズ / P for Pen; release near start to auto-close.
テキスト / TextT → click → type → Enter

Zoom / ズーム   ·   Pan / パン

ActionDesktopMobile / Trackpad
Zoom in / outScroll wheelPinch
Reset viewDouble-clickDouble-tap
Pan canvasHold Space + drag2-finger drag

Move · Rotate · Resize / 移動・回転・リサイズ

S  →  Select tool
   · Click an object (or within 8px of its outline) to grab it.
   · Drag to move.
   · Drag the ○ handle above the bbox to rotate.
   · Drag any of the 8 □ anchors to resize.

Modifier keys while resizing:
   Shift         Lock aspect ratio (corner handles).
   Alt / Option  Anchor to the center (symmetric scale).
   Shift + Alt   Both at once.

Images are aspect-locked on corners by DEFAULT —
Shift releases the lock, so photos never warp accidentally.

Marquee Selection / 範囲選択

Drag on empty canvas  →  dashed selection rectangle.
Any object that the marquee grazes (AABB intersection) → selected.
Then drag any selected object to move the whole group.
The group rotates + resizes like a single object.
Del / Backspace  → delete all selected.
Esc              → deselect all.

§3The Terminal Interface / ターミナル

ENThe terminal is two things at once: an ambient log that hums with system activity, and an interactive prompt that accepts commands. The input is always pinned bottom-right.

History & Recall / 履歴の呼び出し

KeyAction
EnterExecute current input. / 現在の入力を実行。
Recall the previous command. / 直前のコマンドを呼び戻す。
Move forward through history back toward the draft. / 履歴を前進し、元の下書きに戻る。
EscIf USAGE overlay is open, close it first. / オーバーレイを優先して閉じる。

🇯🇵 履歴は最大 80 件まで localStorage に保存 — リロードしても残る。 / 🇬🇧 History persists (up to 80 entries) in localStorage — survives reload.

Built-in Commands / 組込みコマンド

── navigation ──
help / ?                →  in-terminal cheat sheet
usage / man / docs      →  summon the USAGE overlay
clear / flush           →  wipe terminal output
objects                 →  dump every object on the board (debug)

── theme ──
light / dark / gray     →  switch canvas theme

── project (IndexedDB) ──
save <name>             →  snapshot canvas to local DB
ls                      →  list saved snapshots
load <name>             →  restore snapshot

── spell management ──
list                    →  list every registered command (preset / user flagged)
register <n> <js>       →  create a new spell
alias <new>=<old>       →  duplicate a spell under a new name
share <name>            →  copy spell JSON to clipboard
forget <name>           →  remove a user spell (presets protected)

── preset spells ──
$ scatter               →  scatter selected objects randomly
$ grid-3x3              →  place a 3×3 grid at view center
$ biwako-blue           →  retint all strokes to #0044CC
$ fetch-img <kw> <n>    →  summon n images (Lorem Flickr, no key)
$ monoclo               →  grayscale the selected image(s)
$ svg                   →  selection → editable vector SVG

── camera & sharing (v1.2) ──
Black Mirror            →  selfie camera wipe, top-right PIP
Black Mirror off        →  dismiss the camera wipe
share                   →  canvas → shareable URL (modal, no args)

── export ──
export                  →  selection as transparent PNG
export magic            →  spell picker modal
export data             →  bundle export modal (canvas / spells)
export-all              →  one-shot: project + all spells
export-spells [names]   →  one-shot: spells as a package

── easter egg ──
youtube                 →  (try it)

§4Custom Commands / カスタムコマンドの魔法

ENThe single most interesting thing about Black Mirror is that commands travel. Someone writes a spell, posts it on SNS, and with a single paste your board inherits it.

4.1 Importing a spell / 魔法を拾う

STEP · 01 🇯🇵 X / Discord / Slack などで以下のような JSON を見つける。
🇬🇧 Spot a JSON payload like this on social media.
{
  "command": "scatter",
  "action": "const sel = BM.getSelected(); for (const o of sel) BM.translate(o, BM.rand(-400,400), BM.rand(-300,300)); BM.log('scattered ' + sel.length);"
}
STEP · 02 🇯🇵 ターミナル入力欄にペーストする。
🇬🇧 Paste it directly into the terminal input.
STEP · 03 🇯🇵 自動で $<name> として登録される。リロードしても残る。
🇬🇧 Auto-registered as $<name>. Persists across reload.
paste > [JSON payload intercepted]
→ schema check... { command, action } ✓
→ binding $scatter → sandboxed function...
→ persisting to local storage...
[OK] spell acquired — run: $scatter

4.2 Exporting a spell / 魔法を放つ

share <name>
ENCopies the spell's JSON to your clipboard. Paste it on SNS and you've just released a spell into the wild.
[CLIPBOARD] Copied to clipboard. Paste this on SNS!
───────────────────────────────────────────
{
  "command": "scatter",
  "action": "..."
}
───────────────────────────────────────────

4.3 Writing your own / 自作する

register <name> <javascript action>

Inside the action, the BM global is your interface:

MethodWhat it does
BM.getSelected()selected object(s) as an array / 選択中のオブジェクト配列
BM.all()every object on the board / 全オブジェクト
BM.getById(id)lookup a single object by id / ID検索
BM.find(fn)filter objects by predicate / 条件検索
BM.create(type, props?)spawn circle/square/triangle/arrow/text
BM.update(obj|id, patch)bulk-update data attributes / 属性一括更新
BM.translate(obj, dx, dy)nudge an object / 移動
BM.setStroke(obj, color)per-object line color / 個別線色
BM.setFill(obj, color)per-object fill / 個別塗り
BM.remove(obj)delete an object / 削除
BM.clear()wipe the canvas / 全削除
BM.viewCenter(){ x, y } current view center (world coords)
BM.rand(min, max)uniform random number / 一様乱数
BM.getMode()current theme name / 現テーマ
BM.setMode('dark')switch theme programmatically
BM.redraw()repaint after mutations / 変更後の再描画(必須)
BM.save()commit state to undo history / Undo履歴に保存
BM.log(msg, cls?)print to terminal ('t-ok' / 't-err' / 't-dim')
🇯🇵 重要: BM.create('rect', ...)使えません。四角は 'square' を使ってください。対応タイプは circle / square / triangle / arrow / text の5種のみです。
🇬🇧 Important: BM.create('rect', ...) is not supported. Use 'square' instead. The five valid types are circle / square / triangle / arrow / text.

4.4 Aliasing / 別名

alias ripple = scatter
ENDuplicates an existing spell under a new name. Brand the magic with your own naming.

4.5 Import collision behavior / 取り込み時の衝突回避

ENWhen a spell with the same name arrives via paste or drop, existing spells are never overwritten. The incoming spell is renamed to <name>_2026-04-19. Identical payloads are skipped. Presets are always protected.

§4.5Images / 画像の召喚とモノクロ化

$ fetch-img — keyword-based image summoning

ENSummons photos from Lorem Flickr by keyword. No API key required. Multiple images auto-arrange into a grid.
CommandBehavior
fetch-imgdefault keyword "biwako", 1 image
fetch-img mountain"mountain" keyword, 1 image
fetch-img mountain 66 images · auto 3×2 grid
fetch-img tokyo night 9"tokyo night" · 9 images · 3×3 grid
// Technical spec
endpoint : https://loremflickr.com/<w>/<h>/<keyword>?lock=<seed>
size     : 240 × 180 per tile
gap      : 20px
max      : 25 images per invocation
output   : color (use $ monoclo to grayscale after)
grid     : cols = ceil(√n)  ·  rows = ceil(n / cols)
cors     : falls back to URL ref if dataURL conversion fails

$ monoclo — grayscale selected images

ENGrayscales selected image objects. Pair with fetch-img to retroactively monochrome a mood board. Shapes and pen strokes are ignored (explicit error).
// Filter applied
grayscale(100%) contrast(110%) brightness(95%)

// Behavior
· selection required · multi-select supported
· updates obj.data.src to a new dataURL → persisted
· Cmd+Z reverts to the original color image

§5AI-Assisted Spells / AI に魔法を作らせる

ENNon-coders can still author spells — hand Claude or ChatGPT the BM API spec and it returns working JSON. This section is designed to be copy-pasted directly into an AI chat.

5.1 Prompt template / プロンプト雛形

🇯🇵 以下のブロックを AI に貼って、最後の「リクエスト」部分だけ書き換えてください。 / 🇬🇧 Paste the block below into your AI chat and fill in the request at the bottom.

You are a Black Mirror Board spell author.
Return exactly one JSON object in this shape — nothing else:

{
  "command": "name-of-spell (a-z 0-9 _ -, max 30 chars)",
  "action": "javascript source as a string"
}

── BM API (the only functions available inside action) ──
BM.getSelected() / BM.all() / BM.getById(id) / BM.find(fn)
BM.create(type, props?)   // type ∈ {circle, square, triangle, arrow, text}
BM.update(obj|id, patch) / BM.translate(obj, dx, dy)
BM.setStroke(obj, color) / BM.setFill(obj, color)
BM.remove(obj) / BM.clear()
BM.viewCenter()  // {x, y}
BM.rand(min, max) / BM.getMode() / BM.setMode(mode)
BM.redraw()      // call after any mutation
BM.save()        // commits to undo history
BM.log(msg, cls?) // cls ∈ {t-ok, t-err, t-dim}

── Hard rules ──
1. Rect does not exist. Use 'square', not 'rect'.
2. No async/await at the top level — action runs synchronously.
   Use .then() chains if you need Promises.
3. No import / require / external libraries.
4. Always call BM.redraw() after mutations.
5. For meaningful changes, call BM.save() so Cmd+Z works.
6. If the spell requires a selection, guard it:
     if (!sel.length) { BM.log('select first', 't-err'); return; }

── Request ──
(write what you want in one or two sentences)

5.2 Short form / 超短縮版

🇯🇵 上のテンプレが長すぎる場合に。 / 🇬🇧 When the full template is overkill:

Make a Black Mirror Board spell as JSON: {"command":"NAME","action":"JS"}.
BM API: getSelected(), all(), create(type, props), translate(o,dx,dy),
setStroke/setFill(o, c), remove(o), viewCenter(), rand(min,max),
log(msg,cls), redraw(), save().
BM.create types: circle | square | triangle | arrow | text (NO rect).
No async/await. Always end with BM.redraw().
Request: (what you want)

5.3 Example round-trip / 応答例

YOU ↘
Request: 選択中の図形を画面中央に集めて、選択が無ければエラーにする
AI ↘
{
  "command": "center-all",
  "action": "const sel=BM.getSelected();if(!sel.length){BM.log('select first','t-err');return}const vc=BM.viewCenter();for(const o of sel){if(o.data.cx!==undefined){o.data.cx=vc.x;o.data.cy=vc.y}else if(o.data.x!==undefined){o.data.x=vc.x-(o.data.w||0)/2;o.data.y=vc.y-(o.data.h||0)/2}}BM.redraw();BM.save();BM.log('centered '+sel.length+' object(s)')"
}
YOU ↘ 🇯🇵 JSON をコピー → ターミナルにペースト → 自動登録 → $ center-all で実行。
🇬🇧 Copy the JSON, paste into the terminal, auto-registers, run with $ center-all.

5.4 Common AI pitfalls / よくある失敗と対処

Pitfall / 失敗Fix / 対処
AI が BM.create('rect', ...) を書く"rect doesn't exist — use 'square'" と指摘して再生成
BM.redraw() 呼び忘れで画面が更新されない「変更後は必ず BM.redraw()」と強調
action の先頭で async を使う「action is synchronous — use .then()」と伝える
外部ライブラリ (lodash 等) を使おうとする「BM API only, no external libs」と制約を明示
エラーが出るが原因不明エラー文をそのまま AI にコピペで返す

5.5 Growing a spell incrementally / 段階的に育てる

  1. 🇯🇵 最初は最小の魔法を頼む(動作1つ) / 🇬🇧 Start with the smallest version that works.
  2. 🇯🇵 取り込んで実行確認 / 🇬🇧 Register it and verify it runs.
  3. 🇯🇵 「次は選択が無いときの分岐を追加して」 / 🇬🇧 Then ask: "add a guard for empty selection."
  4. 🇯🇵 「次はランダム色を追加して」 / 🇬🇧 Then: "add random color."
  5. 🇯🇵 動く状態を保ちながら機能追加。一気に複雑化しないのがコツ。 / 🇬🇧 Keep it working at every step — avoid big-bang complexity.

§6Tutorial — Your First Command / 最初の一手

ENLet's cast your first spell.
STEP · 01 🇯🇵 画面上に適当な図形を 3〜5 個描いてください(鉛筆でも矩形でも OK)。
🇬🇧 Draw 3–5 objects anywhere on the canvas (pen strokes or squares, both work).
STEP · 02 🇯🇵 ターミナルに打ち込んで Enter
🇬🇧 Type into the terminal and hit Enter:
biwako-blue
🇯🇵 → 全ての線が #0044CC(琵琶湖の静寂を思わせる青)に変わる。
🇬🇧 → Every stroke turns to #0044CC — the blue of Lake Biwa's silence.
STEP · 03 🇯🇵 Select ツールで全部まとめて範囲選択。
🇬🇧 With Select, marquee-drag to grab all objects.
STEP · 04
scatter
🇯🇵 → 選択したオブジェクトがランダムに散らばります。
🇬🇧 → The selected objects fling themselves into random positions.
STEP · 05
grid-3x3
🇯🇵 → ビュー中心に 3×3 のマスが整列配置されます。
🇬🇧 → A crisp 3×3 grid snaps into existence at the view center.
STEP · 06
fetch-img ocean 4
🇯🇵 → 「ocean」で検索した4枚の画像が 2×2 グリッドで配置されます。
🇬🇧 → 4 "ocean" images appear in a 2×2 grid · Lorem Flickr · no API key.
STEP · 07 🇯🇵 画像をマーキーで全選択してから:
🇬🇧 Marquee-select all the images, then:
monoclo
🇯🇵 → 画像が一括でモノクロ化されます。Cmd+Z で戻せます。
🇬🇧 → All selected images grayscale in one pass. Cmd+Z reverts.
🇯🇵 これで、あなたは5つの呪文を既に使ったことになります。
🇬🇧 You've just cast five spells. Now make your own.

Next Steps / 次の一歩

§7Keyboard Reference / キーボード完全版

TOOLS                               THEMES
────────────────────────────────    ────────────────────────────────
S   Select                          dark   Void theme
P   Pen                             light  Surface theme
N   Path (anchor-by-click)          gray   Studio theme (cobalt)
E   Eraser
C   Circle                          COMMANDS
G   Triangle                        ────────────────────────────────
Q   Square                          help      terminal cheat sheet
A   Arrow                           usage     overlay manual
T   Text                            list      list registered spells
I   Upload image                    register  create a new spell
X   Export transparent PNG          share     copy spell JSON
                                    alias     duplicate as new name
WIDTH                               forget    remove a user spell
────────────────────────────────    clear     flush terminal
1   Thin                            ↑ / ↓     command history
2   Medium
3   Thick                           RESIZE MODIFIERS
                                    ────────────────────────────────
GLOBAL                              Shift     lock aspect ratio
────────────────────────────────    Alt/Opt   scale from center
Space     Hold to pan               Shift+Alt both at once
Ctrl+Z    Undo
Ctrl+Y    Redo
Del       Delete selected
Esc       Dismiss / deselect

§8Export & Import / 書き出しと取り込み

ENv1.2 overhauls the export and import story — modal pickers, drag-and-drop, and collision-safe auto-rename.

8.1 Export — 書き出し

CommandOutputFlow
exporttransparent PNGSelection (or all) · Save-As dialog
$ svgBM_CANVAS_*.svgSelection (or all) · editable vector · Illustrator / Figma / Inkscape
shareURL in modalCanvas state → gzip-encoded URL · paste to SNS · recipient opens, board restores
export magic*_SPELLS.jsonModal · checkbox picker · pick any combination
export data*_BUNDLE.json or *_SPELLS.jsonModal · toggle canvas / user / preset
share <name>clipboard JSONOne spell · post to SNS directly
export-all*_BUNDLE.jsonNon-interactive · full bundle
export-spells [names]*_SPELLS.jsonNon-interactive · named or all user

8.1c URL share — share でキャンバス丸ごと送る

ENType share with no arguments and the current canvas gets gzip-compressed, base64url-encoded, and attached to the URL as a #s=... hash. A modal opens with the URL and a copy button. Whoever opens that URL reconstructs your board automatically.
// Typical size
  10 shapes   → ~1 KB
  50 shapes   → ~3 KB
 200 shapes   → ~8 KB   (warning threshold)
 500+ shapes  → 10 KB+  (may break SNS link previews)

// Stripped to keep URL short
· image objects (dataURLs too heavy) → use export data for full fidelity

// Browser requirement
CompressionStream API — Chrome 80+ · Safari 16.4+ · Firefox 113+

// Receiver behavior
open URL → board loads with #s=... → decodes → replaces canvas
         terminal logs "[SYSTEM] shared-state detected"
         URL hash is cleared after load (reload is idempotent)

8.1b SVG export — ベクター書き出しの詳細

ENSelect pen strokes, text, shapes, or images, type svg. The selection (or all objects, if selection is empty) is serialized as standard SVG. The resulting file opens in Illustrator / Figma / Inkscape as editable layered vectors.
// Mapping
circle    → <ellipse>                rotation → <g transform="rotate(...)">
square    → <rect>                   color    → obj.data.stroke / resolveFill(theme)
triangle  → <polygon>                fill     → fill attribute (or none)
arrow     → <line> + <polygon head>  stroke   → stroke attribute + stroke-width
text      → <text dominant-baseline="hanging">
pen       → <path d="M ... Q ...">   (closed strokes carry a final Z + fill)
image     → <image href="dataURL">   (URL-only images: linked, may not load offline)

// Filename
BM_CANVAS_YYYYMMDD_HHMM.svg

// Background note
· Colors come from the current theme. Exporting from DARK mode
  produces light strokes — view on a dark background, or switch
  to LIGHT / GRAY mode first for print-friendly output.

8.2 Import — 取り込み

ENPaste into the terminal, or drop a .json file anywhere on the board. Four payload shapes are auto-detected:
1. single spell
{"command":"grain", "action":"..."}

2. array of spells
[{"command":"a", "action":"..."}, {"command":"b", "action":"..."}]

3. spells package
{"format":"blackmirror.spells", "version":"1.2", "commands":[...]}

4. full bundle (canvas + spells)
{"format":"blackmirror.bundle", "version":"1.2", "project":{...}, "commands":[...]}

8.3 Drop zone

ENDrag a .json file onto the board and the whole screen shows a dashed overlay: "◼ DROP JSON HERE". Drop to auto-detect and register.

8.4 Bundle confirmation dialog

◼ BUNDLE DETECTED
incoming: 12 object(s), 3 spell(s)
current canvas: 5 object(s)

[O] OVERWRITE — replace current canvas with the bundle
[A] ADD       — merge bundle into current canvas (re-IDs incoming)
[C] CANCEL    — do nothing

🇯🇵 完全バンドル(キャンバス込み)を取り込む時だけ、3択の確認ダイアログが出ます。
🇬🇧 The confirmation dialog appears only for full bundles that include canvas objects.

8.5 Collision-safe rename / 衝突時の自動リネーム

SituationResult
Name not takenRegister as-is
User spell exists · identical actionSKIP — already there
User spell exists · different actionRename to <name>_2026-04-19
Rename target also existsAppend _2, _3, …
Collides with presetAlways renamed — preset never overwritten

§9Capabilities & Limits / できること・できないこと

ENKnowing the edges of what's possible up-front — especially when asking an AI to write spells for you — saves a lot of friction.

9.1 BM.create types (the only 5 valid)

TypeRequired props
circle{ cx, cy, rx, ry }
square{ x, y, w, h }   ⚠ not rect
triangle{ x, y, w, h }
arrow{ x1, y1, x2, y2 }
text{ x, y, text, fontSize }

🇯🇵 rect / ellipse / polygon / path / line は未対応。squarecircle(rx≠ry で楕円)・arrow 等で代用してください。
🇬🇧 rect / ellipse / polygon / path / line are not valid. Use square / circle (with different rx/ry) / arrow etc.

9.2 JS features available in actions

FeatureStatus
async / await at top-level❌ action runs synchronously — use .then() chains
import / require❌ no module system
External libraries (jQuery, Lodash, …)❌ not loaded
setTimeout / setInterval✅ works (build your own stop condition)
fetch() / new Image()✅ subject to CORS
document.* / window.*⚠ accessible, but not recommended

9.3 Canvas limits

~500 objects    comfortable
~1000           still smooth
~10000+         likely to stall the browser

Tip: call BM.redraw() once after a bulk loop, not on every iteration.

9.4 External APIs via CORS

ENIf your spell fetches external data, the server must return Access-Control-Allow-Origin: *. Lorem Flickr, Picsum Photos, JSONPlaceholder all work; many others won't.

9.5 Persistence layout

🇯🇵 リロードしてもタブを閉じても残ります。ブラウザのデータを消すと消えるので、重要なワークは export data でファイル書き出し推奨。 / 🇬🇧 Survives reloads and tab-close. Lost if you clear browser data — use export data for hard backups.

9.6 Object data properties / オブジェクト型ごとのプロパティ

🇯🇵 BM.all() で返ってくる各オブジェクトの obj.data で読み書きできる属性。 / 🇬🇧 Attributes available on each object's obj.data, returned by BM.all().

circle    { cx, cy, rx, ry, rotation, fill, stroke, strokeOff }
square    { x, y, w, h, rotation, fill, stroke, strokeOff }
triangle  { x, y, w, h, rotation, fill, stroke, strokeOff }
arrow     { x1, y1, x2, y2, rotation }
text      { x, y, text, fontSize, rotation, fill, stroke }
stroke    { pts: [{x,y}, ...], lw, closed, fill, stroke, strokeOff }  // pen drawing — read-only to BM.create
image     { x, y, w, h, src (dataURL or URL) }                        // via fetch-img or I key
TypeBM.createReadMoveColorRemove
circle / square / triangle
arrow / text
stroke (pen)❌ user-only✅ (v1.2)
image❌ via fetch-img / I

9.7 Use-case feasibility / ユースケース別の可否

Want to doPossibleNote
Clone selected objects / 選択を複製Re-create with same props
Grid / lattice layouts / グリッド配置Loop BM.create
Randomize colors / 色ランダム化BM.setStroke + random hex
Rotate an object / 回転Set obj.data.rotation in radians
Keyword image search / 画像検索Lorem Flickr (CORS OK)
Film-grain texture / フィルムグレインScatter tiny squares
Play a sound / 音を鳴らすnew Audio().play() — user-gesture required
Animate objects / アニメーションsetInterval OK, set a stop condition yourself
PDF / SVG / GIF exportPNG only
Embed video / 動画埋込No video support in image type
Realtime multi-user edit / 共同編集No server — local-only design
Cloud sync / クラウド同期Manual bundle export/import only

§10FAQ / トラブルシューティング

ENCommon problems and their fixes — check here first.

10.1 Drawing / 描画

Symptom / 症状Fix / 対処
🇯🇵 絵が描けない / 🇬🇧 Can't draw🇯🇵 セレクトツール (S) になっている → P でペンに切替 / 🇬🇧 You're in Select — press P for Pen.
🇯🇵 選択しているのに色が変わらない / 🇬🇧 Color change won't apply🇯🇵 v1.2 以前のペン線色バグ。最新 app.html を使う / 🇬🇧 Pre-v1.2 pen-stroke color bug. Reload with the latest app.html.
🇯🇵 描いた絵が全部消えた / 🇬🇧 All my work disappeared🇯🇵 シークレットモードでは保存されない。重要なら save <name>export data を併用 / 🇬🇧 Private/incognito tabs don't persist. Combine save <name> with export data for safety.

10.2 Terminal / ターミナル

SymptomFix
command not found🇯🇵 スペルミス・$ の重複・list で登録名を確認 / 🇬🇧 Typo, duplicated $, or run list to verify the name.
🇯🇵 実行ログは出るが何も起きない / 🇬🇧 Log prints but nothing visible🇯🇵 対象オブジェクトが 0 個、または画面外にいる。objects で確認 / 🇬🇧 Zero targets, or off-screen. Check with objects.
🇯🇵 引数が表示から消える / 🇬🇧 Arguments look truncated🇯🇵 v1.2 以前の表示バグ。最新版で解消済み / 🇬🇧 Pre-v1.2 cosmetic bug — fixed in the current release.

10.3 Spells / 魔法

SymptomFix
unknown type "rect"🇯🇵 BM.create('square', ...) に書き換え / 🇬🇧 Replace with 'square'.
Cannot read property 'x' of undefined🇯🇵 BM.getById の戻り値 null チェックを入れる / 🇬🇧 Null-check the result of BM.getById.
🇯🇵 取り込んだら $grain_2026-04-19 になった / 🇬🇧 Imported spell got a date suffix🇯🇵 既存名を保護する仕様。元を削るなら forget grain、別名にするなら alias grain=grain_2026-04-19 / 🇬🇧 Collision-safe rename. Remove original with forget, or re-point via alias.
🇯🇵 preset を削除したい / 🇬🇧 Want to delete a preset🇯🇵 不可。preset は上書き・削除禁止 / 🇬🇧 Not possible — presets are immutable by design.
🇯🇵 $ fetch-img で画像が出ない / 🇬🇧 No images from fetch-img🇯🇵 user 版が preset を上書きしている可能性 → forget fetch-img → リロード。あるいはオフライン / 🇬🇧 A user spell may be shadowing the preset — forget fetch-img and reload. Or you're offline.

10.4 Export / Import

SymptomFix
🇯🇵 export しても PNG が落ちない / 🇬🇧 PNG doesn't download🇯🇵 キャンバスが空・モバイル Safari は共有シート / 🇬🇧 Canvas might be empty; on iOS the share sheet opens instead of a download.
🇯🇵 D&D しても無反応 / 🇬🇧 Drop does nothing🇯🇵 拡張子が .json でない、JSON が壊れている、または複数ファイル(先頭のみ処理) / 🇬🇧 Wrong extension, invalid JSON, or multiple files dropped (only the first is processed).
🇯🇵 バンドル取り込みで絵が消えた / 🇬🇧 Canvas wiped after bundle import🇯🇵 OVERWRITE を選んだため。Cmd+Z で戻せる場合あり。または最後の saveload で復元 / 🇬🇧 You picked OVERWRITE. Try Cmd+Z, or load your last save.

10.5 Performance / パフォーマンス

SymptomFix
🇯🇵 動作が重い / 🇬🇧 Sluggish🇯🇵 objects で数を確認、1000 超なら分割推奨。画像が多いとメモリを食う / 🇬🇧 Run objects. Split if >1000. Many inline images eat memory.
fetch-img 20枚以上で固まる🇯🇵 Lorem Flickr のレート制限。10枚ずつ取得を推奨 / 🇬🇧 Rate limit — fetch 10 at a time.

10.5b Camera wipe / カメラワイプ

SymptomFix
🇯🇵 Black Mirror と打ってもカメラが出ない / 🇬🇧 Camera doesn't open🇯🇵 file:// だと secure-context 制約で動かない。HTTPS または localhost に / 🇬🇧 file:// blocks getUserMedia. Serve via python -m http.server or HTTPS.
🇯🇵 許可したのに画面が真っ黒 / 🇬🇧 Allowed but video is black🇯🇵 他のアプリがカメラ占有中(Zoom 等)。Black Mirror offBlack Mirror で再試行 / 🇬🇧 Another app (Zoom etc.) holds the device. Cycle off/on.

10.5c Share URL

SymptomFix
🇯🇵 share でモーダルが出ない / 🇬🇧 share does nothing🇯🇵 キャンバスが空 or Safari 16.3 以下(CompressionStream 未対応) / 🇬🇧 Empty canvas or Safari ≤16.3 (no CompressionStream).
🇯🇵 URL が長すぎて SNS で壊れる / 🇬🇧 URL is too long for SNS🇯🇵 オブジェクト数を減らすか export data でファイル共有 / 🇬🇧 Reduce object count or use export data for a file bundle.
🇯🇵 共有 URL を開いたらキャンバスが消えた / 🇬🇧 Receiving a share URL wiped my canvas🇯🇵 Cmd+Z で戻せる場合あり。事前に save <名前> 推奨 / 🇬🇧 Cmd+Z may help; pre-save with save <name>.

10.6 Mobile / モバイル

SymptomFix
🇯🇵 iPhone で入力するとズームされる / 🇬🇧 iPhone auto-zooms on input🇯🇵 2026-04-18 修正済み(font-size 16px 以上に調整) / 🇬🇧 Fixed on 2026-04-18 — use the latest build.
🇯🇵 長押しペーストで魔法が登録されない / 🇬🇧 Long-press paste doesn't register🇯🇵 2026-04-18 修正済み / 🇬🇧 Fixed on 2026-04-18.
🇯🇵 タッチで描画できない / 🇬🇧 Touch drawing doesn't work🇯🇵 Safari の JS が無効の可能性。Chrome for iOS でも試す / 🇬🇧 Check that JavaScript is enabled; try Chrome for iOS as fallback.

10.7 Still stuck? / それでも解決しない

  1. 🇯🇵 DevTools (F12) の Console でエラーログを取得 / 🇬🇧 Open DevTools (F12) → Console to capture the real error.
  2. 🇯🇵 再現手順を添えて SNS (#blackmirror) または GitHub Issues に報告 / 🇬🇧 Report to SNS (#blackmirror) or GitHub Issues with the steps.
  3. 🇯🇵 「こういう魔法が作れない」系は §9 をまず確認 / 🇬🇧 For "spell X won't work" questions, check §9 first.

·