クリエイターガイドライン

HTML・CSS・JavaScript で作品やミニゲームを投稿する人へ。フレームの中で動かすときの制約と、 見る人が気持ちよく遊べるようにするためのコツをまとめました。 文章や Markdown で書く投稿には、ここの内容は関係ありません。

投稿はフレームの中で動く

HTML の投稿は、Framed! 本体とは別のドメイン(投稿ごとに別のオリジン)から配信され、<iframe sandbox="allow-scripts">の中で動きます。フレームの内側はあなたの表現、外側は Framed! の枠です。フレームの中からは、見ている人の Cookie や本体の画面には触れません。

  • 1 枚の HTML 文書に収めます。上限は 262,144 文字 です。
  • <script> と <style> はインラインで書きます。外部のスクリプトは読み込めません(CDN のライブラリも不可)。使いたいものは中身を貼り込んでください。
  • 画像・フォント・音声・動画は、https: の URL と data: / blob: で読めます。 外部の CSS ファイルは読めません。
  • <!doctype html> と、<meta name="viewport" content="width=device-width, initial-scale=1"> を付けてください。
  • 投稿の中のリンクは、どれも新しいタブで開きます(ページ内の # リンクを除く)。

フレームの中で使えないもの

使えないもの代わりに
fetch / XHR / WebSocket などの通信データは HTML に埋め込む。保存は フレーム API のユーザーデータ
localStorage / sessionStorage / IndexedDB / Cookie(触ると例外になる)フレーム API のユーザーデータ。触るなら try で包む
alert / confirm / prompt画面の中に自前のダイアログを描く
<form> の送信submit を止めて JavaScript で処理する
上のウィンドウの移動リンクで新しいタブを開く
カメラ・マイク・位置情報・全画面 API・ポインタロック使わない設計にする

フレームの大きさ

  • フレームの高さは、中身の高さに合わせて伸び縮みします。タイムラインでは 24rem(全画面のフィードでは画面の 75%)で止まり、超えた分はフレームの中でスクロールします。 見る人は「全体を見る」で画面いっぱいに広げられます。投稿のページでは上限なく全体が出ます。
  • 一番外の箱を 100vh / 100dvh で組まないでください。フレームの中の vh はフレームの高さなので、フレームを伸ばすほど中身も伸び、途中で見切られて 16:9 に戻されます。 ゲームの画面は幅から決めるのが確実です。
  • 幅は 320px ほどのスマートフォンから横長の PC まで変わります。固定の幅を持たせず、横スクロールが出ないようにします。
CSS
/* ✕ フレームを伸ばすほど中身も伸びる */
body { height: 100vh; }
/* ○ 幅から高さを決める */
.stage {
width: 100%;
aspect-ratio: 16 / 9;
}

全画面のフィードで避ける領域

全画面のフィードでは、投稿 1 件が画面いっぱいに出て、縦に払うと次の投稿へ進みます。 このとき投稿の上にアプリの UI が重なります。次の領域に、押す必要のある要素や読ませたい文字を固定する(position: fixed / sticky で端に貼り付ける)ことはおすすめしません。

全画面のフィードで、アプリの UI が重なる領域(目安)
  • 上端 60px(+ステータスバーの分): 左右に、隣のタブへ移る導線。タッチの端末では帯全体が払いを受けるので、 投稿には触れません。
  • 右下 幅 72px × 高さ 420px: 前 / 次の投稿・投稿者・いいね・コメント・共有・メニューの列。
  • 左右の端 16px(タッチの端末): 隣のタブへ横に払うための透明な面。
  • スコアや操作ボタンは、上端の帯の下の左側か、下端の左寄りに置きます。
重なりを避ける CSS の例
/* スコアは上端の帯の下、左側に */
.hud {
position: fixed;
top: calc(60px + env(safe-area-inset-top));
left: max(24px, env(safe-area-inset-left));
}
/* 操作ボタンは下端の左寄り。右下の列の幅だけ空ける */
  • 数字は目安です。アプリの UI は変わることがあるので、端にぴったり寄せず、余裕を持たせてください。
  • env(safe-area-inset-*) でノッチやホームバーの分も空けてください(上の例のように)。
  • ゲームの操作面で touch-action: none を使っても、見る人は右下の列のボタンで前後の投稿へ移れます。それでも、操作面以外では払いを奪わないでください(縦に払って次の投稿へ進めるように)。

フレーム API

フレームの中では通信も localStorage も使えない代わりに、スコアボードとユーザーデータを使えます。配信するときに window.framed が差し込まれるので、読み込みの準備は要りません。

使える関数
framed.scoreboard.submit(score, { board, order }) // → { ok, best, rank, improved, order }
framed.scoreboard.top({ board, limit }) // → { ok, entries, total, order }
framed.scoreboard.me({ board }) // → { ok, entry, total }
framed.userData.get(key) // → { ok, value }
framed.userData.set(key, value) // → { ok }
framed.userData.remove(key) // → { ok, removed }
framed.userData.keys() // → { ok, keys }
  • どの関数も Promise を返し、reject しません。失敗は { ok: false, reason } で返るので、必ず ok を見て分岐してください。
  • 使えるのは、サインインしている人が、公開済みの投稿を Framed! の画面で見ているときだけです。未サインインでは unauthenticated、投稿前のプレビューでは unavailable、スペースのサイトなど Framed! の外では 10 秒待って unavailable になります。API が使えなくても遊べるように作ってください。

スコアボード

  • 投稿ごとに、名前を付けて 10 個まで(省くと default)。面ごと・モードごとに分けられます。
  • スコアは整数です。1 人 1 ボードに自己ベストだけが残ります。
  • 並び順は desc(大きいほど上。既定)か asc(小さいほど上。タイムなど)。最初に送ったスコアで決まり、あとから変えられません。
  • top は既定 10 人、100 人まで。行は { rank, score, handle, displayName, isMe } で、同点は同順位です。
  • スコアは自己申告で、正しさは確かめられません。賞品を賭けるような使い方はしないでください。
スコアを送って順位表を出す
<p id="status"></p>
<ol id="ranking"></ol>
<script>
async function gameOver(score) {
const status = document.getElementById("status")
const result = await framed.scoreboard.submit(score)

ユーザーデータ

  • キーと JSON の組です。その人が、その投稿の中でだけ読み書きできます(投稿者にも、ほかの投稿にも見えません)。端末をまたいで残ります。
  • 1 件 64 KB・入れ子 32 段まで。1 人 1 投稿あたり 50 件・合計 1.0 MB まで。
  • 無いキーを get すると value は null です。
進み具合を保存する
<script>
// 進み具合を読み込む。無ければ(初めて・未サインイン)最初から
async function loadProgress() {
const result = await framed.userData.get("progress")
return result.ok && result.value ? result.value : { stage: 1, coins: 0 }
}
// ステージの区切りで保存する(毎フレームは送らない)

名前と回数の上限

  • スコアボードの名前とユーザーデータのキーは、英小文字・数字・ハイフンで 40 文字まで(先頭と末尾にハイフンは使えません)。
  • 書き込み(submit / set / remove)は 1 人 1 投稿あたり 1 分に 30 回、 読み出しは 1 人あたり 1 分に 120 回。1 つのフレームから同時に 4 件まで。毎フレーム・毎操作で送らず、区切りでまとめて送ってください。

失敗の理由

reason意味
unauthenticated見ている人がサインインしていない。
unavailable使えない場所で開かれている(投稿前のプレビュー、スペースのサイト、Framed! の外)。
invalid_request引数の形が違う(名前の文字・スコアが整数でない など)。
rate_limited回数の上限を超えた。少し待ってから送り直す。
busy1 つのフレームから同時に送りすぎた。前の呼び出しの答えを待ってから送る。
order_mismatchスコアボードの並び順が、最初に決まったものと違う。
too_many_boardsその投稿のスコアボードの数が上限に達した。
too_largeユーザーデータの 1 件が大きすぎる。
too_deepユーザーデータの入れ子が深すぎる。
too_many_recordsユーザーデータの件数が上限に達した。
quota_exceededユーザーデータの合計の大きさが上限に達した。
errorFramed! の側の不具合(記録されています)。

順位表に出るのはハンドルと表示名です。フレーム API で保存される情報はプライバシーポリシーに書いています。

禁止・非推奨・推奨

禁止(利用規約 第5条)

  • フレームの隔離(sandbox やコンテンツセキュリティポリシー)を破ろうとすること。本体のドメインでスクリプトを動かそうとする、 ほかの投稿や見ている人のセッションへ届こうとする行為を含みます。
  • Framed! やほかのサービスになりすますこと。フレームの中にサインイン画面や入力欄を模した表示を置き、 パスワードなどを入力させる行為を含みます。
  • 見る人の端末に不利益を与えること。暗号資産の採掘、過度な計算資源の消費、意図しない遷移やダウンロード、 端末の操作を妨げる表示を含みます。
  • 権利を持たない画像・音声・フォント・コードを使うこと。

ほかの禁止事項は利用規約を見てください。

非推奨

  • 全画面のフィードで、アプリの UI が重なる領域に要素を固定すること。
  • 一番外の箱を 100vh で組むこと(フレームの大きさ)。
  • 画面全体の払いを奪うこと(操作面以外での touch-action: none)。
  • 止まらない重い処理。タイムラインには投稿がいくつも並び、同時に動きます。setInterval で回し続けず、requestAnimationFrame を使ってください(画面外では止まるか間引かれます)。
  • 開いた直後に大きな音を鳴らすこと。音は見る人が操作してから鳴らします。
  • 点滅の激しい表示。

推奨

  • スマートフォンの細い幅と PC の広い幅の両方で確かめる。
  • マウスとタッチの両方で操作できるようにする(Pointer Events が便利です)。キーボードでも遊べるとなおよいです。
  • prefers-color-scheme や prefers-reduced-motion に合わせる。
  • 遊び方をフレームの中に短く書いておく。
  • フレーム API が使えないとき(未サインイン・プレビュー)の表示を用意する。

AI で作る(スキル)

Claude などの AI に投稿の HTML を作ってもらうときは、このガイドラインをまとめたスキル(SKILL.md)を渡すと、フレームの制約や避ける領域、フレーム API を踏まえて作ってくれます。 上限の数字は Framed! の実装から作っているので、いつも最新です。

SKILL.md をダウンロード

Claude Code で使う

自分のスキルのフォルダへ置くと、投稿の HTML を頼んだときに自動で使われます。

ターミナル
mkdir -p ~/.claude/skills/framed-post
curl -o ~/.claude/skills/framed-post/SKILL.md https://framed.dog/creators/skill.md

Claude のアプリで使う

framed-post という名前のフォルダに SKILL.md を入れて ZIP にし、設定のスキルから追加します。

ほかの AI で使う

SKILL.md の中身を、作ってほしいものの説明といっしょに貼り付けてください。

できた HTML をそのまま投稿する

AI のアプリに Framed! の MCP サーバをつなぐと、作った HTML を AI からそのまま投稿・書き直しできます。 つなぎ方はユーザーAPIのドキュメントにあります。API から投稿すると下書きを経ずにすぐ公開されるので、先に画面の投稿フォームのプレビューで確かめるのがおすすめです (プレビューではフレーム API は unavailable を返します)。