---
title: "デジタルツイン学習 R2R2R の中身を表紙で伝える ― サムネイル77本を動画と実写に"
slug: r2r2r-thumbnails-video-photo-figure
date: 2026-11-08
day: 470
meta_description: "デジタルツイン学習 R2R2R のシリーズの記事サムネイル77本を、ターミナルの画面写真から、その回の動画・実機の写真・図解に替えた。一覧のカードだけで、各回でアームが何をしたかが分かるようにするため。"
tags: [サムネイル, デザイン, mechArm 270, Unity, YouTube, R2R2R]
series: "ひとり研究所の裏側"
---

**デジタルツイン学習 R2R2R のシリーズの記事サムネイル77本を、ターミナルやエラー画面の画面写真から、その回の動画の1コマ・机の上のロボットアーム（mechArm 270）の写真・その回の図解に替えた。** 記事一覧のカードを見ただけで、各回でアームが何をしたのか、何を測ったのかが分かるようにするためだ。素材の選び方は、動画があれば動画、なければその日の実機の写真、それもなければ図解の図の部分、という順に決めた。

替えたことで、次のことが変わった。

- シリーズのページ（pixelstomotors.com/series2 ）を開いた人が、記事を開く前に、その回のアームの姿勢やブロックの位置を写真で見られる。前は、文字だらけの黒い画面のカードが何本も続いていた
- 77本のうち、10本は YouTube に上げた動画の1コマ、24本は実機の写真、3本は記事の中の画像、29本は記事の図解の一部、11本は記事の図解と同じ見た目で描き起こした図にした
- 作業はひとつのチャットで、最初の依頼から公開まで約1時間半だった（チャットの時刻で数えた。1本あたりの時間は測っていない）。途中で、スマホでシリーズのページがスクロールできない不具合も見つけて直した

サムネイルは、記事一覧のカードの上に出る絵のこと。R2R2R は、iPhone で物を3Dスキャンし、Unity（ゲームを作る道具で、ここではロボットの動きを試す画面）の中で動きを試してから、机の上の mechArm 270 で実際に動かす記録だ。記事の多くは Studio という前のサイトの道具で書いていて、その頃は表紙にその日の作業画面をそのまま貼っていた。そのため、プログラムの実行結果が並んだターミナルの画面が表紙の回が、約50本あった。ロボットアームを動かしているシリーズなのに、一覧を開くと黒い画面に白い文字ばかりが目に入る。

【画像】LOG470_01_series2_before.png 替える前のシリーズのページの一部。LOG213 から LOG190 のあたりで、ターミナルの画面写真やエラーの画面がカードの表紙に並んでいる

やったことの全体は次のとおり。

| やったこと | 本数 | 素材 |
|---|---|---|
| ターミナルの画面写真だった表紙を替えた | 42 | 実機の写真・図解の図の部分・記事の中の画像 |
| 写真も図もない回の表紙を描き起こした | 11 | 記事に書いてあることを、図解と同じ見た目の図に |
| HTML で作った表紙の回も、同じ方針で替えた | 17 | 実機の写真・図解の図の部分・記事の中の画像 |
| YouTube の動画がある回は、動画の1コマに替えた | 10 | 動画の1コマ（そのうち3本は上の回と重なる） |
| スマホでシリーズのページが動かない不具合を直した | 1ページ | 埋め込んだ Google ドキュメントを、ページに直接載せる形に |

替えたあとのページでは、同じあたりにアームとブロックの写真や、図解の図が並ぶ。

【画像】LOG470_02_series2_after.png 替えた後のシリーズのページの同じあたり。LOG204・LOG200・LOG197 などが、実機の写真や図解の図になっている

ターミナルの画面が混ざった表紙は、まだ残っている。LOG209・LOG208・LOG199 のように、写真と画面が半々の表紙と、白黒に加工した画面は、元の表紙のままにした。どこまで替えるかは、次に一覧を見直すときに決める。


## 作業ログ ― 替える回と、使える素材を数えた

最初に、サイトのデータ（GitHub の p2m-site の `src/data/cards.json` と、記事ごとの `src/data/records/*.json`）から、R2R2R のシリーズの記事を数えた。185本あり、そのうち168本は画像の表紙、17本は HTML で作った表紙だった。画像の表紙を全部ダウンロードして並べ、ターミナルやエラーの画面が表紙になっている回を目で拾った。

拾った回について、本文に何が入っているかを数えた。

| 本文にあるもの | 本数 |
|---|---|
| 記事の中に画像がある | 5 |
| 画像はなく、図解（記事に入れた HTML の図）だけがある | 40 |
| 画像も図解もない | 5 |

記事の中の画像も、多くはターミナルや GitHub の画面写真だった。そのまま使える写真があったのは LOG20 と LOG36 の2本だけだった。そこで、Mac の外付けの SSD（KIOXIA）にある実機の記録と、r2r2r-scripts のフォルダにある撮影の画像を、記事の日付ごとに探した。

- `runs/run_pick_place/{回の名前}/` に、1回の試行ごとに上のカメラで撮った PICK（つかむ）と PLACE（置く）の写真がある。6月19日から7月15日の回で使えた
- `r2r2r-scripts/logs/` と `dynamic_tracking_limits/logs/` に、4月から6月の校正やカメラの確認の画像がある
- 3月より前の回には、実機の写真がない。この頃はまだ Unity の中だけで動かしていた


### 見本の3本を、アップと引きで作り直した

最初の見本は、素材をそのまま 16:9 に収めただけだった。サムネイルらしく、アップと引きを使い分けるように言われ、3本を作り直した。

> サムネイルに使うので、アップで作ったり、引きで作ったりとサムネイルらしい構成にしてください。

【画像】LOG470_06_samples_up_and_wide.png 作り直した見本。左から LOG20（アームのレンダーを小さく右に寄せた引き）、LOG232（弧の図の下半分に寄せた図の寄り）、LOG272（ブロックをつかんだ爪先に寄せ、右に置き先の白い箱を入れたアップ）

- ねらい：一覧で並んだときに、カードごとに見る所がはっきりするようにする
- 変わったこと：素材を画面いっぱいに縮めるのをやめ、爪先・箱・図の中心など、その回で見てほしい所に寄せた
- 見込める効果：一覧で同じ大きさの絵が続かなくなる。どれだけ記事が開かれるかは、サイトの計測で確かめる

実機の写真は元の大きさが 1920×1080 なので、爪先だけまで寄せると画質が落ちる。アップは LOG272 くらいの寄り方を上限にした。


### 本人が写った写真は、表紙に使わない

素材を集めている途中で、本人が写った写真は掲載しないように言われた。

> 俺の写真は掲載してはダメ！（もし記事内にあったら削除。）

候補の写真から、人の手が写ったもの（マウスを持つ手）と、カメラの確認で人が写り込んだものを外した。サイトに載っている記事の画像1,026枚と、サイトの中の画像326枚、記事の動画32本から3コマずつを、顔を見つける仕組み（OpenCV の YuNet）で調べた。色の付いた写真で顔が見つかったのは9枚で、人の写真はトップのページの動画の止め絵の1枚だった。これは記事ではなく、トップのページの自己紹介の欄のもので、記事の中には見つからなかった。4コママンガのキャラクターの顔も多く引っかかったが、絵なので外していない。

動画の1コマを表紙にする回でも、LOG113 は動画の画像にアームを持っている人の体が写っていたので、使わなかった。


### 図解の図の部分だけを、高い解像度で切り出した

図解は、記事の中に入れた HTML の図で、ブラウザで開くと動く。丸ごと撮ると、見出しや説明の文が多く、ターミナルの画面とあまり変わらない見た目になる。そこで、Playwright（ブラウザを自動で動かす道具）で図解を開き、図の部分（グラフ・弧・タイムライン・数字）だけを、切り出す幅に合わせて拡大して撮った。幅 560px の範囲を切るときは、1920px になるように約3.5倍で描かせている。画像を拡大するのではなく、図を大きく描かせてから撮るので、線や文字がにじまない。

切り出す位置は、図解を全体で撮って 100px ごとの目盛りを重ね、目で決めた（`dclips.json`）。図の中に絵がなく、文字のカードだけの図解は、その日の実機の写真を使った。


### 似た表紙が並ばないように、隣の回と見比べた

> 残りを作ってください。ただし同じようなデザインが並ばないように工夫してください。

シリーズのページの並び順（LOG の番号の大きい順）に、新しい表紙と、替えない回の表紙を並べた一覧の画像を作り、隣り合う3枚が同じ種類にならないかを見た。写真は、アップ・引き・横から撮った写真・2〜3枚を並べたもの、の4つの構図を回した。図解は、数字の寄り・グラフの寄り・全体の引きを混ぜ、明るい地の図と暗い地の図が続かないようにした。LOG283 は PICK の写真と横から撮った写真を左右に、LOG228 は手で教えた動きの3つのコマを縦の帯で並べた。


## 作業ログ ― 写真も図もない11本は、図解と同じ見た目で描き起こした

LOG21・22・23・29・32・36・74・75・77・104・105 は、ROS 2 の入門や、AI の道具の使い方の回で、写真も図解もない。最初は、大きな文字と太い線のポスターのような絵を描いた。

【画像】LOG470_04_drawn_eleven_first.png 最初に描いた11本。大きな漢字や太い線を使ったポスターのような絵で、写真と図解でできたほかの表紙から浮いている

> なんかサムネイルデザイン全然違いすぎない？

ほかの表紙は写真と記事の図解の一部なので、ポスター調の絵だけが一覧で浮いていた。記事の図解と同じ見た目にそろえて描き直した。地は #f7f7f7 のグレー、白いカード、字は #242424、角丸 8px、小さな英字のラベル。中身は記事に書いてあることだけで組んだ（ノードとトピックの流れ、3段の復旧、Gemini・自分・Cline の役割など）。

> 色が多い。差し色は1色か2色まで

LOG22 の X・Y・Z の軸（赤・緑・青）、LOG32 のコードの色分け（ピンク・黄・オレンジなど）、LOG36 のゲージ（緑・黄・赤）で、色が多くなっていた。差し色を青1色にし、2色目のオレンジは LOG36 の「警告」だけにした。LOG22 は軸を黒にし、上を向く Z だけを青にした。軸の横の文字も、赤・緑の色の名前から、ROS の決まりの向き（X が前・Y が左・Z が上）に書き換えた。

【画像】LOG470_03_drawn_eleven.png 公開した11本。左上から LOG21・22・23・29・32・36・74・75・77・104・105。グレーの地に白いカードで、差し色は青

- ねらい：描き起こした表紙を、写真と図解でできたほかの表紙と同じ種類に見せる
- 変わったこと：ポスター調の絵から、記事の図解を切り出したような見た目にした。差し色を5色以上から青1色（警告だけオレンジ）にした
- 見込める効果：一覧で、描き起こした11本だけが目立たなくなる。見た目の判定はエイイチが一覧で行った


## 作業ログ ― HTML で作った表紙の17本も替えた

公開したあと、シリーズのページの上の段が前のままだと言われた。

> サムネイルアップした？

上の段の LOG297〜LOG284 は、記事ごとに HTML で作った表紙だった。替える回を選んだとき、画像の表紙だけをダウンロードして並べていて、HTML の表紙は中身を見ずに外していた。頼まれたのは、写真や画像、記事の中の制作物を使った表紙に揃えることで、HTML の表紙も対象に入る。LOG250〜258 と LOG284〜297 の17本を、同じ方針で替えた。

【画像】LOG470_08_html_covers_sixteen.png HTML の表紙から替えた回。左上から LOG297・295・294・293・289・288・285・284・258・257・256・255・254・253・252・250。受け渡しのシリーズの回は、7月20日に撮った受け渡しの写真を使った

LOG295 は記事の中のカメラの基板の写真、LOG254 は記事の中の FK テーブル（関節の角度と爪先の位置の対応表）の点の図を使った。LOG253 は、アームの土台を回す関節 j0 を左右に振ったときの写真を2枚並べた。


## 作業ログ ― 動画がある回は、動画の1コマを優先した

> LOG99のサムネイルはYouTubeの画像を使ってサムネイルにして
>
> 動画の画像があれば優先的に使ってください。

記事の本文と図解の中から YouTube の埋め込みを探し、動画のある回を12本見つけた。YouTube が動画ごとに用意している画像（`i.ytimg.com/vi/{動画の番号}/maxresdefault.jpg`、1280×720）を取り、Unity の操作画面の枠・黒い帯・動画を作ったときに入った clideo.com の透かしが入らないように切り出した。

【画像】LOG470_05_video_stills.png 動画の1コマにした10本。上の段の左から LOG37・73・99・120・124、下の段の左から LOG137・149・151・190・251

LOG113 は人の体が写っていたので、LOG71 は動画の画像が Unity とターミナルの画面全体で、アームがとても小さかったので、前の表紙のままにした。


## 作業ログ ― スマホでシリーズのページがスクロールできなかった

作業の途中で、iPhone で series2 のページが下へ動かないと言われた。

> R2のSPページがスクロールできないので確認してください。

原因は、ページの「ドキュメント/事業構想」の欄だった。Google ドキュメントの公開版を iframe（ページの中に別のページをはめ込む枠）ごと埋め込んでいて、その中のドキュメントは幅 800px・高さ 1280px で固定されていた。スマホでは、指でなぞる操作をこの枠が受け取ってしまい、ページ全体が動かなかった。右側が切れて見えたのも、固定の幅のためだった。

ほかのシリーズの要件定義書と同じく、`tools/fetch_series_docs.py` で Google ドキュメントの中身（見出し・段落・表・太字・リンク）を取ってきて、ページに直接載せる形に替えた。iPhone 13 の画面の大きさで開き、ページが 1,496px から 2,296px まで動くことと、横にはみ出さないことを確かめた。

【画像】LOG470_07_series2_doc_iphone.png iPhone の大きさで開いたシリーズのページ。事業構想の文章が、枠ではなくページの中に直接出ている

- ねらい：スマホでシリーズのページを最後まで読めるようにする
- 変わったこと：Google ドキュメントの埋め込み（幅 800px で固定）を、ページの文章として載せる形にした
- 見込める効果：スマホで開いた人が、記事の一覧まで下りられる。スマホからの記事の開かれ方は、サイトの計測で確かめる


## 作業ログ ― 素材を選ぶ順番

素材を選ぶ順番を、図にまとめた。回の種類を選ぶと、どの素材が選ばれるかと、その本数が出る。

【図解】LOG470_cover_source_order.html


## 作業ログ ― 画像をサイトに入れた手順

表紙の画像は、作業の環境で作り、Mac の iCloud の `デジタルツイン学習R2R2R/R2R2R_サムネイル_20261108/` に置いてから、Mac の `upload_media.py` で Cloudflare R2（サイトの画像と動画を置く所）に入れた。サイトのデータの表紙の欄（`cards.json` と記事ごとの json の `cover`）を書き換え、ビルドして公開した。最初に描いた11枚は、同じフォルダの `_不採用/` に移した。

画像の名前は、写真と図解が `r2r2r-thumb-LOG{番号}.webp`、描き起こした11本が `r2r2r-thumb-v2-LOG{番号}.webp`、動画の1コマが `r2r2r-thumb-yt-LOG{番号}.webp`。

### R2 に入れたときの出力

```
入れる 42・入っている 0・名前の重なり 0・合計 4.8MB
  42/42
おわり
```

```
入れる 11・入っている 42・名前の重なり 0・合計 0.3MB
  11/11
おわり
```

```
入れる 11・入っている 53・名前の重なり 0・合計 0.4MB
  11/11
おわり
```

```
入れる 17・入っている 64・名前の重なり 0・合計 2.3MB
おわり
```

```
入れる 1・入っている 81・名前の重なり 0・合計 0.1MB
おわり
```

```
入れる 9・入っている 82・名前の重なり 0・合計 0.9MB
おわり
```

### 公開した変更（p2m-site）

```
53668bc R2R2R（series2）のサムネイル42本を、写真・図解・制作物に差し替え
5661b93 R2R2R（series2）の素材のない11本に、図解と同じ見た目のサムネイルを付ける
358f63b R2R2R（series2）の HTML 表紙だった17本を、写真・図解のサムネイルに差し替え
66cc3b6 LOG99 のサムネイルを YouTube の動画の画像に差し替え
51b4aba series2 の事業構想を、Google ドキュメントの埋め込みからページに直接載せる形へ
37a3e37 R2R2R の YouTube 動画がある9本のサムネイルを、動画の画像に差し替え
```

### 公開したあとの確かめ

公開サイトのシリーズのページの HTML から、新しい表紙の画像の名前を数えた。

```
$ curl -s https://pixelstomotors.com/series2 | grep -o 'r2r2r-thumb-[a-z0-9-]*LOG[0-9]*' | sort -u | wc -l
70
$ curl -s https://pixelstomotors.com/series2 | grep -o 'thumb-yt-LOG[0-9]*' | sort -u | tr '\n' ' '
thumb-yt-LOG120 thumb-yt-LOG124 thumb-yt-LOG137 thumb-yt-LOG149 thumb-yt-LOG151 thumb-yt-LOG190 thumb-yt-LOG251 thumb-yt-LOG37 thumb-yt-LOG73 thumb-yt-LOG99
$ curl -s https://pixelstomotors.com/series2 | grep -c 'series2-1.html'
0
```

70本は、動画の1コマに替える前の数（42＋11＋17）。そのあと動画の10本のうち7本は前の表紙から替えたので、替えた回は全部で77本になる。


### 表紙の素材の一覧

| 素材 | 回 |
|---|---|
| 動画の1コマ（10） | LOG37・73・99・120・124・137・149・151・190・251 |
| 実機の写真（24） | LOG153・170・176・180・182・184・197・200・204・213・217・227・228・230・253・255・258・261・271・272・283・284・285・289 |
| 記事の中の画像（3） | LOG20・254・295 |
| 図解の一部（29） | LOG96・97・100・101・106・129・132・136・144・148・158・172・183・186・188・191・203・226・232・238・246・250・252・256・257・288・293・294・297 |
| 描き起こした図（11） | LOG21・22・23・29・32・36・74・75・77・104・105 |
| 前のまま | LOG71・113 と、画面写真が半々の回・もともと実機の写真だった回 |


## 作業ログ ― 表紙を作ったコード
実機の写真と動画の1コマは、Python の Pillow で切り出した（切り出す位置は、上の「表紙の素材の一覧」の回ごとに、写真に100px ごとの目盛りを重ねて目で決めた）。図解と描き起こした図は、次のファイルで作った。

### survey.js（全文）― 図解を全体で撮り、図の要素の大きさを書き出す

```javascript
const { chromium } = require('playwright'); const fs=require('fs');
const T=JSON.parse(fs.readFileSync('targets.json'));
(async()=>{const b=await chromium.launch();const p=await b.newPage({viewport:{width:1100,height:900},deviceScaleFactor:1});const res={};
for(const [t,v] of Object.entries(T)){for(const [i,e] of v.emb.entries()){
 try{await p.goto('file:///home/claude/p2m-site/public'+e,{timeout:15000});await p.waitForTimeout(700);
 const els=await p.$$eval('svg,canvas,img',ns=>ns.map(n=>{const r=n.getBoundingClientRect();return {tag:n.tagName,x:r.x+scrollX,y:r.y+scrollY,w:r.width,h:r.height}}).filter(o=>o.w>200&&o.h>120));
 const H=await p.evaluate(()=>document.documentElement.scrollHeight);
 res[`${t}_${i}`]={e,els,H};
 await p.screenshot({path:`emb/${t}_${i}.png`,fullPage:true});}catch(err){res[`${t}_${i}`]={e,err:String(err)}}}}
fs.writeFileSync('survey.json',JSON.stringify(res,null,1));await b.close();})();
```

### dclips.json（全文）― 図解から切り出す位置（42本の回のうち図解の22本）

```json
{"96":["96_0",200,150,420],"97":["97_0",214,90,450],"99":["99_0",330,150,440],"100":["100_0",200,190,700],
"101":["101_0",214,580,480],"106":["106_0",214,80,420],"129":["129_0",214,440,300],"132":["132_0",214,350,560],
"136":["136_0",214,95,672],"144":["144_0",214,330,560],"148":["148_0",214,430,672],"151":["151_0",214,820,672],
"158":["158_0",214,380,480],"172":["172_0",214,330,600],"183":["183_0",200,125,560],"186":["186_0",190,60,720],
"188":["188_0",214,640,600],"191":["191_1",250,290,620],"203":["203_0",150,240,760],"226":["226_0",214,200,672],
"238":["238_0",230,230,380],"246":["246_0",160,90,800]}
```

### dclips_fix.json（全文）― 切り出す位置を直した4本

```json
{"129":["129_0",214,290,560]}
```

### dshot.js（全文）― 図解の図の部分を、切り出す幅に合わせて拡大して撮る

```javascript
const { chromium } = require('playwright'); const fs=require('fs');
const C=JSON.parse(fs.readFileSync('dclips.json')); const S=JSON.parse(fs.readFileSync('survey.json'));
(async()=>{const b=await chromium.launch();
for(const [log,[key,x,y,w]] of Object.entries(C)){const dsf=Math.ceil(1920/w*10)/10;
 const p=await b.newPage({viewport:{width:1100,height:1500},deviceScaleFactor:dsf});
 await p.goto('file:///home/claude/p2m-site/public'+S[key].e);await p.waitForTimeout(900);
 await p.screenshot({path:`out/raw_${log}.png`,clip:{x,y,width:w,height:w*9/16}});await p.close();}
await b.close();})();
```

### c17.json（全文）― HTML の表紙だった回の切り出す位置

```json
{"297": ["/embeds/shoot-one-frame-before-building-detection-AAopSn_m.html", "x", 214, 215, 560], "294": ["/embeds/handover-lost-the-incremental-loop-O8EFaTxo.html", "x", 214, 160, 560], "293": ["/embeds/handover-control-code-complete-cP7omSIK.html", "x", 214, 130, 560], "288": ["/embeds/handover-step1-design-Zl3l789a.html", "x", 214, 125, 560], "257": ["/embeds/why-z-correction-failed-twin-poses-EoiUe6St.html", "x", 230, 140, 620], "256": ["/embeds/data-map-before-real-run-IpfCyyU4.html", "x", 200, 165, 580], "252": ["/embeds/which-file-the-live-pick-reads-uusGw3qC.html", "x", 214, 170, 560], "250": ["/embeds/real-data-first-digital-twin-6tlwNejn.html", "x", 214, 75, 560], "251": ["/embeds/arcs-without-area-Sn9SA1es.html", "x", 0, 0, 1100]}
```

### c17b.json（全文）― LOG251 の切り出す位置（点の雲の図）

```json
{"251": ["/embeds/arcs-without-area-Sn9SA1es.html", "x", 200, 170, 640]}
```

### c17c.json（全文）― LOG250・LOG252 の切り出す位置を直したもの

```json
{"250": ["/embeds/real-data-first-digital-twin-6tlwNejn.html", "x", 200, 85, 700], "252": ["/embeds/which-file-the-live-pick-reads-uusGw3qC.html", "x", 214, 230, 560]}
```

### s17.js（全文）― HTML の表紙だった回の図解を撮る

```javascript
const { chromium } = require('playwright'); const fs=require('fs');
const C=JSON.parse(fs.readFileSync('c17.json'));
(async()=>{const b=await chromium.launch();
for(const [k,[e,_,x,y,w]] of Object.entries(C)){const p=await b.newPage({viewport:{width:1100,height:1400},deviceScaleFactor:Math.ceil(1920/w*10)/10});
await p.goto('file:///home/claude/p2m-site/public'+e);await p.waitForTimeout(900);
await p.screenshot({path:`out17_raw_${k}.png`,clip:{x,y,width:w,height:w*9/16}});await p.close();}
await b.close();})();
```

### drawn_eleven_gen.py（全文）― 描き起こした11本の HTML を書き出す

```python
H='''<!doctype html><html><head><meta charset="utf-8">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">
<style>*{margin:0;padding:0;box-sizing:border-box}
html,body{width:640px;height:360px;overflow:hidden;background:#f7f7f7}
body{font-family:'Inter','Noto Sans JP',sans-serif;color:#242424;-webkit-font-smoothing:antialiased}
.w{width:640px;height:360px;padding:28px 34px;position:relative;display:flex;flex-direction:column;justify-content:center}
.lab{font-family:'Inter',monospace;font-size:10px;letter-spacing:.14em;color:#888;text-transform:uppercase}
.h{font-size:17px;font-weight:700;margin:6px 0 16px}
.card{background:#fff;border-radius:8px;padding:14px 16px}
.dark{background:#242424;color:#fff;border-radius:8px;padding:14px 16px}
.row{display:flex;align-items:center;background:#fff;border-radius:8px;padding:13px 16px;margin-bottom:8px}
.n{font-family:Inter;font-size:11px;color:#999;width:34px}
.tag{margin-left:auto;font-family:Inter;font-size:10px;letter-spacing:.1em;color:#999}
.chip{display:inline-block;border:1px solid #e6e6e6;border-radius:4px;padding:3px 8px;font-size:11px;background:#fff}
.blue{color:#2f6fdf}
</style></head><body><div class="w">'''
E='</div></body></html>'
P={}
P['LOG21']='''<div class="lab">ROS 2 · NODE GRAPH</div><div class="h">カメを動かす信号は、トピックを流れている</div>
<div style="display:flex;align-items:center;gap:12px;margin-top:30px">
<div class="card" style="width:150px"><div class="lab">NODE</div><div style="font-weight:700;margin-top:4px">teleop_turtle</div></div>
<div style="flex:1;height:2px;background:#2f6fdf;position:relative"><div style="position:absolute;right:-2px;top:-5px;border-left:10px solid #2f6fdf;border-top:6px solid transparent;border-bottom:6px solid transparent"></div></div>
<div class="dark" style="width:170px"><div class="lab" style="color:#aaa">TOPIC</div><div style="font-family:Inter;font-weight:600;margin-top:4px;font-size:13px">/turtle1/cmd_vel</div></div>
<div style="flex:1;height:2px;background:#2f6fdf;position:relative"><div style="position:absolute;right:-2px;top:-5px;border-left:10px solid #2f6fdf;border-top:6px solid transparent;border-bottom:6px solid transparent"></div></div>
<div class="card" style="width:130px"><div class="lab">NODE</div><div style="font-weight:700;margin-top:4px">turtlesim</div></div></div>
<div class="card" style="margin-top:34px;font-family:Inter;font-size:12px;line-height:1.8;color:#555">$ ros2 topic echo /turtle1/cmd_vel<br><span class="blue">linear: {x, y, z} &nbsp; angular: {x, y, z}</span></div>'''
P['LOG22']='''<div style="display:flex;gap:16px;height:100%">
<div class="card" style="flex:1.3;position:relative"><div class="lab">RVIZ2 · AXES</div>
<svg width="300" height="250" style="position:absolute;left:30px;top:40px">
<g stroke="#e6e6e6">'''+''.join(f'<line x1="{20+i*26}" y1="230" x2="{90+i*12}" y2="120"/>' for i in range(10))+''.join(f'<line x1="{20+ (90-20)*t:.0f}" y1="{230-110*t:.0f}" x2="{254+(198-254)*t:.0f}" y2="{230-110*t:.0f}"/>' for t in [0,.25,.5,.75,1])+'''</g>
<line x1="140" y1="175" x2="250" y2="200" stroke="#242424" stroke-width="5"/><line x1="140" y1="175" x2="60" y2="210" stroke="#242424" stroke-width="5"/><line x1="140" y1="175" x2="140" y2="40" stroke="#2f6fdf" stroke-width="5"/><circle cx="140" cy="175" r="5" fill="#242424"/></svg></div>
<div style="flex:1;display:flex;flex-direction:column;gap:8px;justify-content:center">
<div class="row"><span style="width:12px;height:12px;background:#242424;border-radius:2px"></span><b style="margin-left:12px">X</b><span class="tag">前</span></div>
<div class="row"><span style="width:12px;height:12px;background:#242424;border-radius:2px"></span><b style="margin-left:12px">Y</b><span class="tag">左</span></div>
<div class="row"><span style="width:12px;height:12px;background:#2f6fdf;border-radius:2px"></span><b style="margin-left:12px">Z</b><span class="tag">上</span></div>
<div class="lab" style="margin-top:6px">REP-103 · RIGHT-HANDED</div></div></div>'''
P['LOG23']='''<div class="lab">UNITY ⇄ ROS 2</div><div class="h">脳と体をつなぐ</div>
<div style="display:flex;align-items:stretch;gap:0;margin-top:26px">
<div class="dark" style="flex:1;padding:26px 22px"><div class="lab" style="color:#aaa">BRAIN</div><div style="font-size:26px;font-weight:700;margin-top:8px">ROS 2</div><div style="font-size:12px;color:#bbb;margin-top:6px">計算・指令</div></div>
<div style="width:130px;display:flex;flex-direction:column;align-items:center;justify-content:center">
<div style="width:100%;height:2px;background:#2f6fdf"></div><div style="font-family:Inter;font-size:12px;color:#2f6fdf;margin-top:8px">TCP :10000</div></div>
<div class="card" style="flex:1;padding:26px 22px"><div class="lab">BODY</div><div style="font-size:26px;font-weight:700;margin-top:8px">Unity</div><div style="font-size:12px;color:#888;margin-top:6px">mechArm を召喚</div></div></div>'''
P['LOG29']='''<div class="lab">DIGITAL TWIN · NEXT STEPS</div><div class="h">次に挑む3つの実験</div>
<div class="row" style="background:#242424;color:#fff"><span class="n" style="color:#aaa">01</span><b>Python でシーケンス制御</b><span class="tag" style="color:#aaa">EXP 1</span></div>
<div class="row"><span class="n">02</span><b>Unity ➔ ROS 2 の双方向同期</b><span class="tag">EXP 2</span></div>
<div class="row"><span class="n">03</span><b>リアルタイム・テレオペ</b><span class="tag">EXP 3</span></div>'''
sw=[('#2f6fdf','予約語','def · class · if'),('#f2f2f2','関数・メソッド','publish()'),('#b5b5b5','文字・数値','"link1" · 0.5'),('#6b6b6b','コメント','# メモ'),('#d9d9d9','変数・クラス名','node · msg')]
P['LOG32']='<div class="lab">PYTHON · SYNTAX COLORS</div><div class="h">色分けは、役割の地図</div><div style="display:flex;gap:14px"><div class="dark" style="flex:1;font-family:Inter;font-size:12px;line-height:2;padding:16px 18px;background:#242424">'+'<br>'.join(f'<span style="display:inline-block;width:{w}px;height:8px;border-radius:4px;background:{c}"></span>' for c,w in [('#2f6fdf',60),('#f2f2f2',110),('#b5b5b5',80),('#6b6b6b',140),('#d9d9d9',70),('#2f6fdf',40),('#f2f2f2',90)])+'</div><div style="flex:1.3">'+''.join(f'<div class="row" style="padding:9px 14px;margin-bottom:6px"><span style="width:12px;height:12px;border-radius:3px;background:{c};border:1px solid #ddd"></span><b style="margin-left:12px;font-size:13px">{t}</b><span class="tag" style="text-transform:none;letter-spacing:0">{s}</span></div>' for c,t,s in sw)+'</div></div>'
P['LOG36']='''<div class="lab">HRI · CONFIDENCE GAUGE</div><div class="h">確信度を、色で周辺視野に伝える</div>
<div class="card" style="margin-top:30px;padding:26px 22px"><div style="display:flex;height:22px;border-radius:11px;overflow:hidden">
<div style="flex:5;background:#2f6fdf"></div><div style="flex:3;background:#a9c1ef"></div><div style="flex:2;background:#e4572e"></div></div>
<div style="display:flex;margin-top:12px;font-size:12px"><div style="flex:5">安心</div><div style="flex:3">注意</div><div style="flex:2">警告</div></div></div>
<div class="lab" style="margin-top:16px">HudManager.cs</div>'''
P['LOG74']='''<div class="lab">ROLES · GEMINI × HUMAN × CLINE</div><div class="h">三者の線引き</div>
<div style="display:flex;gap:12px;margin-top:24px">
<div class="card" style="flex:1;padding:22px 18px"><div class="lab">STRATEGY</div><div style="font-size:22px;font-weight:700;margin-top:8px">Gemini</div><div style="font-size:12px;color:#888;margin-top:6px">設計する</div></div>
<div class="dark" style="flex:1;padding:22px 18px"><div class="lab" style="color:#aaa">DECISION</div><div style="font-size:22px;font-weight:700;margin-top:8px">自分</div><div style="font-size:12px;color:#bbb;margin-top:6px">決める</div></div>
<div class="card" style="flex:1;padding:22px 18px"><div class="lab">IMPLEMENT</div><div style="font-size:22px;font-weight:700;margin-top:8px">Cline</div><div style="font-size:12px;color:#888;margin-top:6px">実装する</div></div></div>'''
P['LOG75']='''<div class="lab">RECOVERY PROTOCOL</div><div class="h">不具合の大きさで、戻し方を変える</div>
<div class="row"><span class="n">01</span><b>作業単位の差し戻し</b><span class="tag">GIT</span></div>
<div class="row"><span class="n">02</span><b>シーン単位の復旧</b><span class="tag">UNITY</span></div>
<div class="row" style="background:#242424;color:#fff"><span class="n" style="color:#aaa">03</span><b>完全復旧</b><span class="tag" style="color:#aaa">GOOGLE DRIVE</span></div>'''
P['LOG77']='''<div class="lab">COST · CLINE</div><div class="h">定額と従量課金の壁</div>
<div class="card" style="margin-top:10px;padding:18px 18px 14px"><svg width="540" height="170">
<line x1="30" y1="150" x2="530" y2="150" stroke="#e6e6e6"/><line x1="30" y1="10" x2="30" y2="150" stroke="#e6e6e6"/>
<line x1="30" y1="85" x2="520" y2="85" stroke="#242424" stroke-width="3"/>
<polyline fill="none" stroke="#2f6fdf" stroke-width="3" points="'''+' '.join(f'{30+i*5},{150-0.0055*(i*5)**1.6:.0f}' for i in range(0,99))+'''"/>
<text x="440" y="78" font-size="12" font-family="Noto Sans JP" fill="#242424">定額（Pro）</text>
<text x="400" y="22" font-size="12" font-family="Noto Sans JP" fill="#2f6fdf">従量（API）</text>
<text x="34" y="166" font-size="10" font-family="Inter" fill="#999" letter-spacing="1.4">TOKENS →</text></svg></div>'''
P['LOG104']='''<div style="display:flex;gap:14px;height:100%">
<div class="dark" style="flex:1.25;padding:18px 18px;display:flex;flex-direction:column;background:#1b1b1d">
<div style="display:flex;align-items:center;gap:10px"><img src="face-joy.webp" style="width:30px;height:29px;border-radius:6px;background:#fff"><b style="font-size:13px">ハコブくん</b></div>
<div style="background:#2c2c30;border-radius:8px;padding:12px 14px;margin-top:14px;font-size:12px;line-height:1.7">ハコブくんにおまかせ！<br>ロボット開発の記録、全部頭に入ってるよ。<br>なんでも聞いてね！</div>
<div style="margin-top:12px;display:flex;flex-wrap:wrap;gap:6px">
<span class="chip" style="background:transparent;color:#fff;border-color:#555">このプロジェクトって何をやってるの？</span>
<span class="chip" style="background:transparent;color:#fff;border-color:#555">失敗したことその原因を教えて</span>
<span class="chip" style="background:transparent;color:#fff;border-color:#555">一番苦労したのはどこ？</span></div></div>
<div style="flex:1;display:flex;flex-direction:column;justify-content:center;gap:8px">
<div class="lab">HAKOBU · AI AGENT</div>
<div class="row"><span class="n">01</span><b style="font-size:13px">ナレッジベース</b></div>
<div class="row"><span class="n">02</span><b style="font-size:13px">チャットフロー</b></div>
<div class="row"><span class="n">03</span><b style="font-size:13px">カスタムUI</b></div></div></div>'''
P['LOG105']='''<div class="lab">REPOSITORIES · 2</div><div class="h">コードも記事も、戻せる状態に</div>
<div style="display:flex;gap:12px;margin-top:22px">
<div class="card" style="flex:1;padding:22px 18px"><div class="lab">REPO 1</div><div style="font-size:20px;font-weight:700;margin-top:8px">コード</div><div style="font-size:12px;color:#888;margin-top:6px">Unity · Python</div></div>
<div class="card" style="flex:1;padding:22px 18px"><div class="lab">REPO 2</div><div style="font-size:20px;font-weight:700;margin-top:8px">コンテンツ</div><div style="font-size:12px;color:#888;margin-top:6px">記事 · 図解</div></div></div>
<div class="dark" style="margin-top:12px;display:flex;align-items:center"><span style="font-family:Inter;font-weight:600">CLAUDE.md</span><span style="margin-left:auto;font-size:12px;color:#bbb">AI エージェントへの決まりごと</span></div>'''
for k,v in P.items(): open(k+'.html','w').write(H+v+E)
```

### drawn_eleven_shot.js（全文）― 描き起こした11本を 1920×1080 で撮る

```javascript
const { chromium } = require('playwright');
(async()=>{const b=await chromium.launch();const p=await b.newPage({viewport:{width:640,height:360},deviceScaleFactor:3});
for(const k of ['LOG21','LOG22','LOG23','LOG29','LOG32','LOG36','LOG74','LOG75','LOG77','LOG104','LOG105']){
await p.goto('file:///home/claude/work/t11b/'+k+'.html',{waitUntil:'networkidle'});await p.waitForTimeout(400);await p.screenshot({path:k+'.png'});}
await b.close();})();
```
