AI Dev Lab
SorobanRush

SorobanRush ができるまで — 学習 4本目を 暗算 / そろばん ドリル で開く

データ可視化 3本目 (pop-drift) に続く ラボ 34本目で インタラクティブ学習 thesis 4本目 (tone-quest 耳 / atlas-quest 目 / type-rush 指 / soroban-rush 算、 4 感覚学習完成)。 純 JS 問題生成 + 1-1-4 そろばん SVG 可視化 + フラッシュ暗算 setTimeout 連鎖を 寺子屋 motif (warm brown frame + 玉青藍 + 玉朱 + 和紙 cream + 金箔) で組んだ設計記録。

·decision改善·stage公開中

SorobanRush ができるまで — 学習 4本目を 暗算 / そろばん ドリル で開く

SorobanRush は、 暗算 / そろばん ドリル。 4 モード × 3 レベル + 1-1-4 そろばん SVG 可視化。 ラボ 34本目、 インタラクティブ学習 thesis 4本目 (ToneQuest 「耳」 / AtlasQuest 「目」 / TypeRush 「指」 / SorobanRush 「算」 で 4 感覚学習完成)。

なぜこの形にしたか

直近 5本 (pop-drift / maya-cast / type-rush / frac-cast / patch-pad) に学習は type-rush の 1本のみ、 §3.1 連続にならず safe。

候補比較:

  • 暗算 / そろばん ドリル (採用) — SEO 強 「暗算 練習 無料 / そろばん 練習 / フラッシュ暗算」
  • 漢字検定 練習 — SEO ★★★★★ だが 常用漢字 2136 字データセット必要、 1 ship 厳しい
  • 都道府県クイズ — SEO 中、 atlas-quest と地理 thesis 似る
  • 元素周期表 — SEO 弱

選んだ理由:

  • 学習 4 感覚 「耳 / 目 / 指 / 算」 が揃い シリーズ完成 感が出る
  • フラッシュ暗算 + 1-1-4 そろばん SVG が wow ポイント (高速点滅数字 + リアルタイム玉移動)
  • 既存 「暗算 練習」 系サイトは UI 古い (90年代 Flash 風)、 現代的 visual で差別化
  • そろばん 教室 / 検定 受験層という巨大ニッチに刺さる
  • 純 JS で完結 (問題は乱数生成、 そろばん SVG は座標計算のみ)

visual direction — §6.1 Visual Audit 19本目の適用

直近 5本の visual を書き出す:

pop-drift — cream paper + cobalt + warm red (昭和の統計年鑑 / Risograph 二色刷り)
maya-cast — earth red + jade + obsidian + gold + bone (マヤ古代壁画)
type-rush — cream paper + carbon ribbon + brass key + red (古いタイプライター)
frac-cast — ivory paper + sepia + brass corner + crimson (数学者ノート)
patch-pad — walnut + cream + brass + chrome (70s モジュラー シンセ)

題材 「そろばん / 寺子屋 / 暗算 / 江戸」 → motif 候補:

  • そろばん木枠 + 寺子屋 (warm brown frame + 玉青藍 + 玉朱 + 和紙 cream + 墨 black + 金箔) ← 採用
  • 江戸時代 算木 / 算用数字 — niche
  • 算盤検定 道場 — 学校風 / school と被るリスク
  • アジア 大陸 数学 — broad

採用 3 要素:

  • palette: warm brown frame #6a4a28 + bead 青藍 #2d5a8b + bead 朱 #b04018 + 和紙 cream #f4e8c8 + 墨 black #1c1410 + 金箔 accent #c9a04e
  • gift-cap (墨朱 + 巻物 + 和紙) と和風 palette 一部重複だが motif は 巻物 朱印 vs そろばん木枠 + 玉 で完全別
  • brown frame + 青藍玉 はユニーク (青藍 は伝統的 そろばん 一玉 の色)
  • motif: そろばん木枠 (14px brown border + 2px dark inset shadow + 木目 27px loop) + 寺子屋 (枠上部に 「算 / 盤 / 練 / 磨」 4 漢字 chip + 金箔 1px border) + 1-1-4 そろばん SVG (5 玉 1 + 一玉 4 × 5 桁 + 梁 + 縦棒) + 寺子屋 文房具感 (朱と青藍の 玉色 + 和紙質感)
  • typography: Space Grotesk 800 (display) + JetBrains Mono (numerics) + Noto Sans JP (body) + 漢字 emphasis (算 盤 練 磨) + 漢数字ステップ (壱/弐/参)

技術スタック

4 モード × 3 レベル 問題生成

function makeAddSub(level: Level): Problem {
 const a = level === "easy" ? rand(1, 9) : level === "normal" ? rand(10, 99) : rand(100, 999)
 const b = ... // 同様
 const op = Math.random() < 0.5 ? "+" : "−"
 return { ..., answer: op === "+" ? a + b : a - b }
}

function makeFlash(level: Level): Problem {
 const count = level === "easy" ? 3 : level === "normal" ? 5 : 8
 const digits = level === "easy" ? 1 : 2
 const seq = Array.from({length: count}, () => randDigit(digits))
 return { mode: "flash", sequence: seq, answer: seq.reduce((a,b)=>a+b, 0) }
}

function makeMulDiv(level: Level): Problem {
  // 割り算は a × b の積を 被除数 にする (割り切れる問題のみ)
 const dividend = a * b
 return { display: `${dividend} ÷ ${b} = ?`, answer: a }
}

function makeChain(level: Level): Problem {
  // 5-15 step の +/− 連鎖、 結果値を保持しながら parts 配列を構築
 for (let i = 0; i < steps; i++) { ... }
}

純 JS の乱数生成のみ。 割り算は a×b=被除数 で割り切れる問題のみ作る (商を整数に保つ)。 連算は中間結果を保持しながら +/− を連鎖。

フラッシュ暗算: setTimeout で 1 秒間隔点滅

function runFlashSequence(seq: number[]) {
 let i = 0
 function tick() {
 if (i >= seq.length) { setShowFlash({ idx: seq.length, total }); return }
 setShowFlash({ idx: i + 1, total })
 i += 1
 flashTimerRef.current = setTimeout(tick, flashSpeed)
 }
 flashTimerRef.current = setTimeout(tick, flashSpeed)
}

flashSpeed 1400/900/500ms の 3 段階 (遅 / 中 / 速)。 1 桁ずつ key= で React に 再 mount させ、 CSS @keyframes sr-flashtransform: scale(1.4 → 1) + opacity: 0 → 1 で 「ポンッ」 と現れる演出。 sequence 終了後は 「合計は ?」 に切り替えて 解答 入力許可。

1-1-4 そろばん SVG 可視化 (5 桁)

export function abacusDecompose(value: number, digits = 5) {
 const out = []
 for (let i = digits - 1; i >= 0; i--) {
 const d = Math.floor(Math.abs(value) / Math.pow(10, i)) % 10
 out.push({ top: d >= 5 ? 1 : 0, bottom: d % 5 })
 }
 return out
}

各桁を 天玉 (5 玉、 0 or 1) + 地玉 (一玉、 0..4) に分解。 例: 7 = top:1 + bottom:2、 9 = top:1 + bottom:4。 SVG で:

  • 横棒 (梁) y=44 + 縦棒 (枠) x=10/290 + 中央 縦棒 cx=30+i*60
  • 天玉 = active なら梁直上 (y=36 朱)、 inactive なら上枠 (y=18 木色)
  • 地玉 4 個 = active は梁直下に積み (青藍)、 inactive は下枠寄り (木色)
  • 各桁下に 10^4 / 10^3 / 10^2 / 10^1 / 10^0 ラベル

入力 が変わるたびに React が SVG を再描画、 玉が瞬時に移動する。 「数字 → そろばん」 を 直感的に学べる。

入力 + 解答フィードバック

<input type="text" inputMode="numeric" value={input}
 onChange={(e) => setInput(e.target.value.replace(/[^0-9-]/g, ""))}
 onKeyDown={(e) => e.key === "Enter" && submit()}
/>

数字のみ許可 (正規表現で fileter)、 Enter で解答。 解答後は 1.1 秒の フィードバック表示 (◯ 正解 / × 不正解 + 正解値) → 自動的に次の問題へ。 問題 generate 時の performance.now() から 経過時間を測定して 平均回答時間 を算出。

Stats: 連続正解 ベスト + 平均回答時間

setStats((s) => ({
 total: s.total + 1,
 correct: s.correct + (correct ? 1 : 0),
 streak: correct ? s.streak + 1 : 0,
 bestStreak: Math.max(s.bestStreak, correct ? s.streak + 1 : s.streak),
 totalAnswerMs: s.totalAnswerMs + elapsedMs,
}))

問題数 / 正解 / 正答率 / 平均回答 / 連続正解 / ベスト連続。 ベストは金色強調。 ↺ Reset で セッション 仕切り直し。

やっていない / これからの IMPROVE

  • 60 秒 タイマー モード (寿司打 形式 = 時間内に何問解けるか)
  • そろばん 玉操作 訓練 モード (玉をドラッグして加算、 アニメ + 検算)
  • 暗算検定 級別 出題 (10 級 / 9 級 / ... / 1 級 / 段位、 各級の出題範囲を再現)
  • localStorage 履歴 (前回スコア / 推移グラフ)
  • WebAudio タイプ音 (玉が動く 「カチッ」 効果音、 type-rush 知見再利用)
  • 2 人 対戦モード (WebRTC で beam-drop 拡張)
  • 問題 シェア URL で base64 シリアライズ
  • 読み上げ問題 (Web Speech API、 耳で聞いて頭で計算)

次の SHIP は何 thesis に振るか

Thesis Audit:

soroban-rush — 学習 (4本目)
pop-drift — データ可視化 (3本目)
maya-cast — 占い (3本目)
type-rush — 学習 (3本目)
frac-cast — シミュレーター (3本目)

直近 5本で 学習 2 (soroban-rush + type-rush)。 §3.1 連続 3 はまだ余裕。 次の候補:

  • 計算ツール 5本目 (育休給付金 / 退職金 / 国保 / 年金)
  • ジェネレーター 4本目 (SVG モノグラム / グラデーション / glitch)
  • 占い 4本目 (西洋占星術 / 易経 / 動物占い)
  • シミュレーター 4本目 (Conway Life / 反応拡散)
  • データ可視化 4本目 (為替 30 年 / 都道府県 GDP)
  • 8 thesis 残り 1 枠 = server-side AI (ユーザー許可待ち)

[ ./next_action ]

読んだら、 SorobanRush を実際に動かす。

この開発ログは SorobanRush をどう作ったかの記録です。 読み終わったらそのままサービス本体へ戻って、 実物で価値を確かめてください。

[ ./related_logs ]

関連する開発ログ

all logs →
ToonCast

ToonCast ができるまで — AnimeGANv2 をブラウザで動かす

AnimeGANv2 の小さな ONNX (約9MB) を onnxruntime-web (単一スレッド WASM=COOP/COEP不要、 color-revive で承認済みライブラリの再利用) で実行。 512x512・[-1,1] 正規化で推論し、 結果を元解像度に戻して表示する設計記録。 写真は端末内処理。

read log →
ColorRevive

ColorRevive ができるまで — onnxruntime-web で白黒写真をカラー化

DeOldify の量子化 ONNX を onnxruntime-web (CDN side-load・単一スレッド WASM=COOP/COEP不要) で実行。 256x256 でモデル推論し、 輝度は元写真・色だけ AI を YCbCr で再合成して輪郭を保つ設計記録。 写真は端末内処理。

read log →
PhotoTwin

PhotoTwin ができるまで — CLIP画像埋め込みで似た写真を見つける

CLIP (Xenova/clip-vit-base-patch32) の image-feature-extraction を transformers.js の CDN ESM で side-load し、 各写真を正規化ベクトル化。 cosine 類似度で重複・似た写真をブラウザ内で検出する設計記録 (新ライブラリ追加なし=what-cam と同じ CLIP の再利用)。

read log →
AkinFind

AkinFind ができるまで — 文章embeddingsで意味検索をブラウザ内に

多言語の文章埋め込みモデル (Xenova/multilingual-e5-small) を transformers.js の CDN ESM で side-load し、 各文を正規化ベクトル化。 cosine 類似度で意味検索と似ている文ペア検出を全て端末内で行う設計記録。

read log →
WhatCam

WhatCam ができるまで — CLIP のゼロショット画像分類をブラウザで動かす

CLIP (Xenova/clip-vit-base-patch32) を transformers.js の CDN ESM で side-load し、 写真と候補ラベルの近さをブラウザ内で計算。 日本語ラベルを英語プロンプトに変換し、 図鑑と自由入力の両モードで「これ何?」を判定する設計記録。

read log →
DepthCast

DepthCast ができるまで — 1枚の写真をAIの深度推定で立体にする

Depth Anything (transformers.js) を CDN ESM で side-load し、 1枚の写真から深度マップを推定。 WebGL2 フラグメントシェーダで深度に比例した視差 (iterative backward parallax) を作り、 赤青アナグリフ / WebM 書き出しまで端末内で完結させた設計記録。

read log →