魔法の書式Spell format

1行のJSONで、ボードの上で動くものを作れる。作ったものは書き出して人に配れる。
AIに書かせるためのプロンプトも用意してある。
A spell is one line of JSON that makes something happen on the board — and it travels as text, so you can hand it to someone. A ready-made prompt for AI is included.

JSON · one line · no build step

まず動く例を1つ0. Lead with a working example

中央から🔥を15個ばらまいて、外へ飛ばしながら小さくして、最後に自分で片づける魔法。これをコピーして、形を真似るのが一番早い。A spell that scatters 15 🔥 from view center, animates them outward while shrinking, then cleans up after itself. Copy this and match its shape.

{"command":"fire","action":"const N=15,vc=BM.viewCenter();const sparks=[];for(let i=0;i<N;i++){const s=BM.create('text',{x:vc.x,y:vc.y,text:'🔥',fontSize:20+Math.random()*20});sparks.push({o:s,vx:(Math.random()-0.5)*15,vy:(Math.random()-0.5)*15});}let f=0;const id=setInterval(()=>{if(f++>40){clearInterval(id);for(const s of sparks){const i=BM.all().indexOf(s.o);if(i>=0)BM.all().splice(i,1);}BM.redraw();BM.log('fire: extinguished.');return;}for(const s of sparks){s.o.data.x+=s.vx;s.o.data.y+=s.vy;s.o.data.fontSize*=0.92;}BM.redraw();},30);BM.log('fire: burst of 15 sparks.');"}

ここから読み取れること:What this teaches you:

魔法には2種類あるTwo kinds of spell

この頁が説明しているのはコードで書く魔法(action型)。もう1つ、図形を置くだけの魔法(ops型)がある。ops型は置ける形が決まっていて(square / sticky / text / circle / triangle / arrow)、コードは一切実行されない。STOREでは「図形を置くだけ」と「コードで動く」を分けて表示していて、コード型は掲載前に審査を通している。This page describes the code kind (action). There is also a shapes-only kind (ops), limited to a fixed set of shapes (square / sticky / text / circle / triangle / arrow) with no code execution at all. The STORE labels the two separately, and code spells are reviewed before they are listed.

選んだものを spell 名前 で保存すると ops 型になる。動きを付けたいときだけ action 型を書く。Saving a selection with spell name produces an ops spell. Write an action spell only when you want motion.

書式1. The format

{"command":"NAME","action":"JAVASCRIPT_CODE_AS_A_STRING"}

他の項目は無い。1行で出力する(Markdownの囲みや前置きの文章は付けない)。There are no other fields. Output a single line — no Markdown fences, no leading prose.

使ってはいけない言葉2. Forbidden / hallucinated terms

⚠️次の語は他のシステム(ゲームのプラグイン等)のもので、BM BOARD では意味を持たない。AIがよく混ぜてくるので、出てきたら書式から外れている合図。⚠️These belong to other systems (game plugins, RPG engines) and have no meaning in BM BOARD. If you find yourself reaching for any of them, you are off-spec.

internalName, displayName, description, icon, category, cost, mp, cooldown, actions (as array), PROJECTILE, particle, onHit, effect, range, power, type (top-level), magic create, /bmboard, /reload, plugins/, skills/, give, server-side, mod, plugin, manifest

action の中で使える BM.*3. The BM.* API

⚠️ここに無いものは使えない。AIに書かせるときは、この表をそのまま渡すのが確実。⚠️Nothing outside this table is available. When prompting an AI, hand it this table verbatim.

SymbolMeaning
BM.log(msg, cls?)Print to terminal. cls is 't-ok' / 't-err' / 't-dim' / undefined.
BM.viewCenter()Returns {x, y} — current canvas view center in world coordinates.
BM.create(type, props)Adds a new canvas object. Returns the object.
BM.translate(obj, dx, dy)Move obj by delta.
BM.update(idOrObj, patch)Patch obj's data with given properties.
BM.remove(obj)Remove obj from canvas.
BM.setStroke(obj, color)Set stroke color.
BM.setFill(obj, color)Set fill color.
BM.all()Array of all canvas objects (mutable — use .splice to remove).
BM.getSelected()Array of currently-selected objects.
BM.clear()Wipe all canvas objects.
BM.redraw()Force a repaint. Call after every mutation.
BM.save()Push a history snapshot for undo.
BM.rand(min, max)Random number in [min, max].
argsString passed at invocation. $name foo barargs === 'foo bar'.

置けるものと、その設定4. Object types and props

typeprops
circle{cx, cy, rx, ry, rotation, fill, strokeOff}
square{x, y, w, h, rotation, fill, strokeOff}
triangle{x, y, w, h, rotation, fill, strokeOff} or {pts:[{x,y},{x,y},{x,y}], rotation, fill}
arrow{x1, y1, x2, y2, rotation}
text{x, y, text, fontSize, rotation}
sticky{x, y, w, h, text, color, rotation} (default color: '#FFE873')

もう少し例5. More working examples

いちばん短いもの — 中央に黄色い付箋を置く5.1 Minimal — drop a yellow sticky at view center

{"command":"hi","action":"const c=BM.viewCenter();BM.create('sticky',{x:c.x-100,y:c.y-100,w:200,h:200,text:'hello'});BM.redraw();BM.log('placed.');"}

同じ名前で始めて止める(トグル)5.2 Toggle pattern (start / stop on the same command)

{"command":"snowy","action":"if(window._snowyId){clearInterval(window._snowyId);window._snowyId=null;BM.log('stopped.');return;}let i=0;const c=BM.viewCenter();const items=[];for(let k=0;k<40;k++){const o=BM.create('text',{x:c.x+Math.random()*1200-600,y:c.y-400-Math.random()*200,text:'❄',fontSize:14+Math.random()*10});items.push({o,vy:0.5+Math.random()*1.2});}window._snowyId=setInterval(()=>{if(i++>10000){clearInterval(window._snowyId);window._snowyId=null;return;}for(const it of items){it.o.data.y+=it.vy;if(it.o.data.y>c.y+400){it.o.data.y=c.y-400;it.o.data.x=c.x+Math.random()*1200-600;}}BM.redraw();},30);BM.log('snowy: running. $snowy again to stop.');"}

選択中のものを使う — 選んだ全部の色を変える5.3 Use selection — recolor every selected object

{"command":"red","action":"const sel=BM.getSelected();if(!sel.length){BM.log('select first','t-err');return;}for(const o of sel){BM.setStroke(o,'#FF0000');}BM.redraw();BM.log('recolored '+sel.length);"}

登録のしかた6. How a spell is registered

登録すると、⌘K に名前で並ぶ。forget 名前 で消せる。Once registered it appears in ⌘K by name. Remove it with forget <name>.

AIに書かせるためのプロンプト(英語のまま使う)7. AI agent system prompt

⚠️これは訳さずそのまま貼る。API名や禁止語は英語が正なので、日本語に直すと精度が落ちる。最後の行だけ、作りたいものを日本語で書いてよい。⚠️Paste this as-is. The API names and banned terms are canonical in English. Describe what you want on the last line.

You are creating a "spell" for BMBoard, a single-page canvas web app.

OUTPUT FORMAT (mandatory, no exceptions):
{"command":"NAME","action":"JAVASCRIPT_CODE_AS_STRING"}

Output 1 line, no Markdown, no explanation, no code fences.

Reference example (study this shape — yours should look the same):
{"command":"fire","action":"const N=15,vc=BM.viewCenter();const sparks=[];for(let i=0;i<N;i++){const s=BM.create('text',{x:vc.x,y:vc.y,text:'🔥',fontSize:20+Math.random()*20});sparks.push({o:s,vx:(Math.random()-0.5)*15,vy:(Math.random()-0.5)*15});}let f=0;const id=setInterval(()=>{if(f++>40){clearInterval(id);for(const s of sparks){const i=BM.all().indexOf(s.o);if(i>=0)BM.all().splice(i,1);}BM.redraw();return;}for(const s of sparks){s.o.data.x+=s.vx;s.o.data.y+=s.vy;s.o.data.fontSize*=0.92;}BM.redraw();},30);"}

The action is RAW JS executed via `new Function('BM','args',action)`.
Available API:
- BM.log(msg, cls?)
- BM.viewCenter() → {x, y}
- BM.create(type, props)   // type: 'circle'|'square'|'triangle'|'arrow'|'text'|'sticky'
- BM.translate / update / remove
- BM.setStroke(obj, color), BM.setFill(obj, color)
- BM.all() / BM.getSelected() / BM.clear() / BM.redraw() / BM.save()
- BM.rand(min, max)
- args                     // string passed at call time

Object props:
- circle:   {cx, cy, rx, ry, rotation, fill}
- square:   {x, y, w, h, rotation, fill}
- triangle: {x, y, w, h, rotation, fill}
- arrow:    {x1, y1, x2, y2, rotation}
- text:     {x, y, text, fontSize, rotation}
- sticky:   {x, y, w, h, text, color, rotation}

NEVER use: internalName, displayName, description, icon, category, cost, cooldown,
mp, projectile, particle, onHit, range, power, type-as-top-level-field,
plugins, skills, magic create, /bmboard, /reload.
Those are not BMBoard.

Now create a spell: <DESCRIBE WHAT YOU WANT>

配るSharing

作った魔法は1つのJSONとして書き出して人に渡せる。設定 → 魔法の 📤 から提出すると、自動の一次審査(禁止API・BM APIが実在するか・構文・名前の重複)を通って STORE の一覧に載る。サーバーは無く、費用もかからない。A spell exports as a single JSON you can hand to someone. Submitting it from Settings → Spells (📤) runs an automatic first review — banned APIs, whether each BM API actually exists, syntax, name collisions — and then it lands in the STORE list. No server, no cost.

この頁の機械可読な写し8. Reference