「定点読書」の仕組み

youtube.com

RSVP(単語を一つずつ同じ場所に出す読み方)をAIで作った話が盛り上がっていた。 https://togetter.com/li/2751150

自分も少し前に、同じ「視線を動かさない」から出発して別の方向に行った読み方を作っていたので、仕組みを書いておく。Box-Maison というサービスのブックリーダーに入っている「定点読書」という機能で、Web(bmaison.cc)では2026年8月から動いている。モバイルアプリ版も作っていて、以下ではそちらの話も混ざる。特許を取る予定はない。なお、冒頭の動画はアプリ版。

RSVP との違い

RSVP は速く読むための道具で、単語を止めて見せる。定点読書は速さが目的ではなく、声の速さで読む。

画面の一点に注視点があり、そこを本文が流れる。ここまでは同じ。違うのは、単語を一つずつ出すのではなく、本文が1本の帯になって連続して流れること。縦書きなら縦の帯が上から下に、横書きなら横の帯が右から左に。

流れる速さを決めているのは読み上げ音声。TTS が文を読み、その声に合わせて帯が進む。声が句点で息を継げば帯も止まり、次の文に入れば動き出す。目は声の少し後ろをついていく。

帯の後ろには本物のページが半透明で見えている。帯が進むと後ろのページも同期してスクロールし、今読んでいる文がハイライトされる。文字だけが動いているのではなく、ページの上を自分が進んでいるのが分かる。RSVP の「今どこを読んでいるのか分からなくなる」問題への対処でもある。

仕組み

1. 帯

段落を折り返しなしの1行(縦書きなら1列)にレイアウトし、注視点の位置まで平行移動させて描く。段落の切れ目には少し隙間を置く。縦書きは自前のレイアウトエンジン、Web は writing-mode: vertical-rl の1列 DOM を transform で動かしている。

2. 声がペースメーカー

文単位で TTS に読ませ、TTS から取れる情報(発話開始、単語境界イベント)を足場にする。単語境界イベントは環境によって来たり来なかったりするので(Android Chrome の Google TTS はほぼ来ない)、足場だけに頼らず、読む速さを学習して間を補間する。

  • 読速の学習は「文の重み付き長さ ÷ その文の発話にかかった実時間」。文字ごとに重みを持たせ、句点 3.5、読点 2、閉じ括弧 1.8、普通の文字 1。声は句読点で間を置くので、等速で流すと声が黙っている間に帯が先行して見える。重みでそれを吸収する。
  • 学習値は再生速度 1.0 倍に正規化して保存し、使うときに現在の倍率を掛ける。
  • 足場と足場の間はデッドレコニング(学習した速さでの推定)で埋める。
  • 推定が声より先に行ったら文末で止まって待つ。後ろなら追いつく。文の中では巻き戻さない。
  • 推定は速め側に倒す。帯は文末で止まるので、速すぎる推定は待つだけで済むが、遅すぎる推定は遅れが蓄積する。この非対称に気づいてから安定した。

3. 滑らかに流すと読めない

最初は推定位置に向かって滑らかに帯を動かしていた。これは同期精度を上げても読みにくかった。

目が文字を読むときの動きは連続追跡ではなくサッケード(跳んで、止まる)で、滑らかに流れる文字を追うと滑動追跡を強いられて、文字認識に向かない動きになるため。

なので内部では小数位置の推定を持ちつつ、表示の目標は「文節っぽい塊」の頭に量子化する。塊の切れ目は句読点、スペース、かなから漢字への遷移、漢字2字以上からかな2字以上への遷移、最長6文字。目標が次の塊に移ったら指数追従(時定数はおよそ 1/5.5 秒)で寄せる。結果として「跳んで、減衰しながら止まりかけて、また跳ぶ」という動きになる。

モバイル版では Android の TTS が形態素単位で単語イベントをくれるので、最初から自然にそうなっていた。Web ではそれを手で模倣した。実機の画面録画をフレーム解析して、モバイル版の動きが「40〜80px のバースト+長い指数減衰の尻尾」であることを確かめ、Web の時定数をそこに合わせた。

注視点の直下にある文字だけを毎フレーム橙色に塗る。塊の頭ではなく、常に「注視点に今いる一文字」。

4. 紗越しの本物のページ

帯は黒い半透明の紗の上に白文字で描く。紗の下では普通のリーダー画面がそのまま動いていて、帯の進行に合わせてスクロールし、今読んでいる文がハイライトされる。

一時停止すると紗ごと消えて普通のリーダーに戻る。スクロールして好きな文をタップすればそこから再開する。再開位置の優先順位は「タップした文 > 自分でスクロールした位置の段落 > 読みかけの文をもう一度」。

使ってみて一番効いたのはここだった。文字が動いているのではなく、自分がページの上を進んでいると分かる。

5. その他

  • 縦書き・横書きの両方に対応。横書きの帯は英数字を単語単位のセルにする。
  • 画面内で90度回転して、帯を画面の長辺に走らせるモードがある。
  • TTS の言語は文ごとに判定する(英語の本の中の日本語引用も読める)。
  • 縦と横を両方試すと、横書きの方が明らかに効果が大きかった。縦に流れる文字は没入が切れた瞬間に酔いやすい。

分かったこと

  • 音声同期の精度と読みやすさは別物。目の動き方に合わせないと、同期が合っていても読めない。
  • 推定を間違えるなら速め側に。
  • 全体が動いているのが見えると安心する。帯だけが動くと不安になる。

追記

生成AIとソフトウェアエンジニアのニヒリズムについて

大規模言語モデル(LLM)を中心とした生成AIの出現に伴い社会が作り変えられつつある。個人的な感覚としてはAIベンダーがリリースする定期的なモデルの刷新は2023年のGPT-4から本質的な変化はないと思っていた。だが、2025年末からのClaude Codeを先頭にした性能向上や、多様な角度での機能の拡充などが、複合的に作用しあい、2025年末以降、本格的な動きとなっている。

我々にとって生成AIは最初の1、2年はチャットを通して新しい独白、つまり内面世界を拡張するものとして、半ば暗黙的な存在のように扱われてきた。しかし、APIやプラグインの充実により、スタンドアロン性が強かったWordやExcel、そしてコードエディタとも接続されるようになった。

独白の延長であったならば、AIの出力がどんなに優れていようともーー逆にハルシネーションを含んだものであったとしてもーーそれをどのように扱うかは単にユーザーの「やる気の問題」であった。しかし、仕事道具と直結されたならば、いよいよ避けて通るのが難しくなっていく。

そこで、本稿では、まずAIがプログラミングの何を解決しているのかを確認し、その上で私たちの中のなにが変わろうとしているのかについて考えてみることとしたい。

生成AIと言葉のパラダイム

 生成AIが得意とするのは言語処理全般である。もちろん、生成AIはマルチモーダル化によって言語以外の形式も入出力として扱えるようになっている。だが、これは生成AIが言語以外を扱えるということを意味するものではない。マルチモーダルは言語以外のデータを連続的なベクトル列、すなわち言語と同構造として扱う技術の確立によって実現されている。それゆえ、生成AIが得意とするのは言語処理のみであると言っても差し支えない。

 この言語処理にはもちろんプログラミング言語も射程内に置かれる。なぜならAIが学習しているものは言葉の順番と組み合わせからなるシンタックスであるからだ。シンタックスとは表面的には文法によって規定された言葉の配列のことである。だが、それだけではない。言葉と言葉がどのように結合するのかという問いには、単なる文法の枠組みだけではなく、パラダイム(=共有された常識)が不可分なものとして埋め込まれているのである。

 例えば「SNSが人心を荒廃させる」のは近年ティーンエイジャーに対するSNS規制と共に、いくつかの国家で採用されつつあるパラダイムだ。このような言葉は「BeRealは盛らなくていいから気が楽」という日常会話と対応してもいる。これらが統計的なデータ量によって学習されるとき、生成AIは我々の社会のパラダイムをも学びとっていく。

 これと全く同じプロセスで「atan2は2点間を結んだ線の角度を求める」ことを生成AIは知っている。数学的な真理も、言葉で定義されている以上はパラダイムであり、同様に「2つの引数を取るアークタンジェント」に多くのプログラミング言語でatan2という命名がされていることもまたパラダイムであるからだ。このような知識が言葉によって我々の間を媒介する以上、どんなものであれ学習は可能となってしまう。

 プログラミングとは、作業者がある情報操作に対する展望を持ち、それを具体的なコードへと秩序化していく試行である。「試行」と書いたのは、秩序化を行うには必ず秩序化の対象となる混沌が存在するからだ。作業者は具体的な状況という混沌に身を置きながら、無数にある(ように見える)選択肢から実装を削り出し、コードによる秩序化を施していく。この営みは、抱える混沌の量に多寡はあるとしても、常に不確定要素を孕んだ「試み」であったと言えるだろう。

 しかし、前述のパラダイムを含んだシンタックスが生成AIの学習下におかれることにより、今やこのプロセスは自動的に解決されつつあるものとなった。もはや「試み」とはいえなくなりつつあるのであって、ここに、職業としてのソフトウェアエンジニアの在り方が揺るがされているのである。

 とはいえ実際のところ、ソフトウェアエンジニアという職業が直ちに危うくなるとはあまり考えられない。列車の運行、銀行口座の管理、スーパーのレジで使われるシステム、普段触れているウェブアプリケーションなど、我々の周りの多様なソフトウェアが今後も必要とされ存在し続けるのであってみれば、「餅は餅屋」の論理は働き続けるだろう。

 そうだとすれば、「ソフトウェアエンジニアという職業は終わった」と一足飛びに構える必要はないのかもしれない。だが、彼らの本懐であったプログラミングがもはや冒険的な「試み」ではなくなったのだとして、生成AIが彼らの抱えていた荷物を羽根のように軽くしてしまったのだとして、その精神構造の変化に、処方箋とは言わないまでも何らかの秩序化を施しておきたいと考えたのである。

問いの時代とニヒリズム

 荷物が軽くなったエンジニアたちに与えられた新しいパラダイムは、「AIに対して何を問うか」であった。今や「開発」はボトルネックではなく、AIへの入力となる適切な問いをデザインできる人がより一層求められるというわけだ。

 確かに自らの内から問いを形成することは、エンジニアにとって重要な資質の一つである。これについては、プログラムコードは「応答」に属するものであるという整理が可能だろう。よくプログラミングの初学者に対して「プログラミングは作りたいものがあると上達が早まる」と言う人がいるのは、プログラムは常に問いと表裏の関係にあって、応答単体では存在しえないものであるからだ。

 それゆえに経験を重ね、実力のあるエンジニアの設定する問いは的確でかつクリティカルなものとなる。だが、だからこそそこに行き止まりとしてのニヒリズムが生じているのではないか。

 それについて考えたい点は二つある。第一に問いとは解体を待つものであるということだ。例えば、エンジニアが生業とする「Development」は知っての通り『開発』を意味する。また他にも写真を『現像』するという意味でも使われる。これは露光によって撮影された「潜像」を「Development」することでネガフィルムを作成する工程を指す。Developmentの語源はラテン語の「dis-(分離) + volvere(包む、巻く)」であり、事物が秘める可能性を外部に向けて不可逆的に「展開」するというニュアンスを持っている。

 ソフトウェア開発も同様だ。ある仕様を実現するために書かれたプログラムは、機能が追加されるたびにそれと矛盾する機能の可能性を失うし、コードが増え複雑になれば、全体の整合性を保ったまま改変を加える余地が狭まっていく。再設計による全体の秩序化を試みても、今度はその設計から逸脱する仕様に対しては堅固に閉じることになる。このプロセスがAIによって自動的に、そして速やかに行われるとき、我々は問いが形成された端からその姿を失う断崖の先端に立たされているように感じるのではないか。

 第二に、我々が考える問いの姿はもはや従来のものではなく、発達する技術の側面に張り付くものであるということだ。問いとは、元々は私たちの認知に生じる違和感や危機の予感から生じるもので、それで十分行動に移せるものであった。だが、この時代で求められている問いとは、AIのプロンプトになるべきものだ。そして、技術が発達するごとに、つまりAIに投げる指示は抽象的なもので十分となるごとに、問いの姿はいわば縮退していくのである。

 冒頭で述べたように、具体的な作業に代わって問いを形成することは今や社会の要請だ。だが、作り出した問いは直ちに解体されてしまう。しかもその問いはAIへの入力でしかなく、技術の発展によって立ち位置すら浮動していく。こうして無力感と虚無感が積み重なり、精神もまた縮退の憂き目を見るのである。

問いの構造

 「これからは問いを言語化するスキルが重要だ」、「問いを立てることが人々に残された責務だ」——これらは近年支配的なトレンドだ。だが前のセクションで述べたように、問いが、今後も驀進を続けるAIと我々の境界面となることを考えれば、ここに自らのアイデンティティを仮託し続けるのは危うい。

 ではどうするべきか。もちろん処方箋を示すことはできない。しかし、このように考えてみるのはどうだろうか。

 「もはや『問い』も生産されるべきものだ」と。

 これは実装コストが限りなく小さくなったのだから、問いをランダムに、あるいは網羅的に生成して、それをAIに対するプロンプトとすればよい、と言いたいのではない。だが、まずは我々を取り巻く状況を具体的な図に示そう。

図1: 問いの構造

 図1は問いの構造を図にしたものだ。左へ抽象に向かう線が伸びており、右に行くほど具象に向かっていく。問いを立てるのは、この軸上のどこかに点を打ち、その点を起点に思考を展開することだ。例えば筆者は過去に掲示板を作ったことがあるので、「掲示板システムを作る」という問いを立てるとしよう。左に行くのは「なぜ」と動機を遡行する動きであり「SNSに疲れたから」や、「使っていた掲示板が使えなくなったから」という問いに変化する。右に行くのは「どうやって」と目的を具象化する動きであり、「SNS利用層にも響くデザイン」や、「DDoSや負荷に強いインフラ設計」といった問いに変化する。

 もちろんAIが得意なのは右に向かう具象化である。左に向かう抽象化もできないわけではないが、AIには動機の源泉となる記憶が存在しないため、何らかのペルソナを仮設した上で、この場合は「掲示板が好きなAさん」の生い立ちを創作したうえで解釈をするという操作が必要になるだろう。

図2:問いの構造(二次元的な拡張)

 図2は図1の一次元的な構造を二次元的に拡張したものである。画像上部が抽象、下部が具象であり、左右には前後の経路の方向性が広がっている。先ほどの『DDoSや負荷に強いインフラ設計』であれば、トラフィックの遮断、認証の差し挟み、キャッシュの配置など、複数の方向性が左右に並びながら奥へと枝分かれしていく。具象化が進むにつれ、これらはより直接的な手法へと姿を変えていく。エンジニアはこの広がりを見渡しながら、奥へと進む経路を定めていくのである。

 しかし、ある程度奥に進むと、つまり問いの具象化が進行していくと、選び取った経路は技術によって高速に処理されてしまう。熟練エンジニアがAIを用いて時に数十倍の効率を発揮することができるのは、問いをAIによって解決が可能なところまで追い込むことが可能なためだ。

 さて、この構造を示した上で、二つ考えたいことがある。第一に熟練エンジニアのアドバンテージは、AIの処理能力が抽象側に前進することによって容易に失われてしまいうる、ということだ。コーディングを省略したVibe Coding(バイブコーディング)は、熟練者であるほど高効率を叩き出し、自身の身体感覚が拡張されたような快感すら覚えることがある。このような状況は、これまで技術理解、設計、他者への説明、もちろん実装を怠らなかったエンジニアに与えられる特権だと言ってよい。だが一方で、AIが登場するタイミングという偶然に恵まれただけのことでもある。それゆえ時間をおいてそのアドバンテージが相対化されていくことは免れようがない。

 第二に、経路を選びながら問いを具象化していく作業は、求められる問いが抽象側に移動したとしても再現できる可能性があるということだ。例えば初心者や未経験者がAIを用いて開発を行う場合は、自分で経路を選ぶことができず、AIの差し出す提案に呑まれていくことになる。これを「自動的決断」と呼ぶことにしよう。対して、熟練者であれば数々の経験から開発の勘所を知っており、主体的に落とし穴を避けて具象に向かっていくことができる。これは「計画的決断」だと言えるだろう。この「計画的決断」こそがブレイクスルーなのだと主張したいのではない。だが、もし何を問いにして良いかわからない事態が到来しても、AIの支援があれば「自動的決断」を避けながら「計画的決断」によって問いを「生産」していくこと、これを工学的に再現可能なものとして構想することは成り立つのではないか、と考えたいのである。

 今後はこれまでより問いが抽象方向に移動し、そこが主戦場となることは明白だ。だからこそ「生き様」や「ワクワク」などの根本的な部分から湧き立つ感情が取り沙汰される。これらは図1の左側の極点——超抽象とも呼ぶ領域、真善美に接する場所——により近い問いであるようにも見える。

 だが、図で示したように、超抽象とAIが取り扱う領域は、地続きではあっても無限遠ともいえる関係にあるのであって、問いが抽象側に移動したとしても、直ちに「超抽象」で勝負になるというのには飛躍の気配を覚え、注意を挟みたくなる。

 とはいえプログラミングも含む、言葉に関する具象の領域が自動化されつつあるのはこの時代の現実なのであって、私たちに耐える場所があるのだとすれば、浮動する具象の先端から一定の距離を取りながら、AIを片手にエンジニアリングを行っていくしかないのだと思われる。

ブラウザで動くボイスチェンジャー機能付きのチャットルーム

を作った。

bugvoice.ssuwam.workers.dev

作ろうと思ったきっかけ

前からWebRTCを使って何かを作ってみたいとは思っていて、色々と調べていた。

その上で、Wasmを使えば高品質とは言わないまでも実用レベルのボイスチェンジャーは十分に動くだろうし、それを組み合わせると、手軽に匿名性を付与した通話環境が作れて面白いんじゃないかなあというのが作ったきっかけ。

作ってみて思ったこと

ボイスチェンジャーを通した人の声って思ったよりしんどく、雑談とかゲームとかで長時間聞き続けるのは辛いかも。

そもそも声だけの時点でボイスチェンジャー通さずともまあまあ匿名性ってあるのかなあとも思った、マイクとかの癖で声って変わるし。

そういう意味ではあんまり有用性を感じなかったんだけど、最初は信用できないからボイスチェンジャー越しで話して、打ち解けてきたらオフにする、というようなユースケースは想像できる、かも。

というわけで、すごくニッチだけど「ボイスチャット ボイスチェンジャー ブラウザ」とかで検索した人に引っかかってくれると嬉しく思う。もちろん試作レベルなので実際に使ってみて繋がりが悪くてもご勘弁。

技術情報

  • サーバー環境はCloudflare Workers を使っていて、
  • チャットルームの状態管理はCloudflare Durable Objectsが行っている。
  • ボイスチャットはWebRTCを使っていて、シグナリングもDurable Objectsを使っている。
  • ボイスチェンジャーはRustで書かれたピッチシフトとフォルマントシフトのコードが
  • WebAssemblyにビルドされブラウザから呼び出される。
  • フロントエンドはSolid Startを採用している。

終わりに

ロゴがカッコいいのと、あと略称のBVがほとんど被らないのがいい感じ。

ちなみに僕はフーリエ変換とかもよく知らない。

おわり

トップ画面
ボイスチェンジャーテスト画面
チャットルーム画面

「URLが好き」

妻と食事をしていたときにこの話をしたら、爆笑されたことがあった。

「URLが好きなんだよね」と言ったら笑われた、という話ではない。

もう少し理屈っぽい話をしたうえで、結局そういうことだったのか、と笑われたのだ。

セマンティックウェブとURL

僕がエンジニアとして働き始めた頃、大向一輝先生(現・東京大学、当時は国立情報学研究所)から案内があり、研究所で行われるセマンティックウェブの勉強会に参加したことがある。

また、その頃にはセマンティックウェブ関連の案件にいくつか関わる機会もあり、しばらくのあいだ、セマンティックウェブは僕の創作活動の中心にあった。

セマンティックウェブとは、物事の意味情報を本当にウェブ空間に存在させるにはどうしたら良いか、という問いに応えるための技術群である。

そこで重要になるのが、言語に左右されない一意な名前空間だ。

例えば「鉛筆」は英語では pencil とも表記でき、そのどちらもデータの上では単なる文字列に過ぎない。しかし、Wikipedia の「鉛筆」のURLであれば他の言語とも互換性があり、ページを開けば画像も含めた鉛筆にまつわるさまざまな情報にアクセスできるので、意味論的には価値が高いといえるわけだ。

このように、ウェブ空間で一意性といえば、URLによる名前空間をおいて他にない。今思えばセマンティックウェブという観点から、URLに対する愛着が無自覚ながら始まっていたように思う。

サブドメインのうれしさ

その後、受託の仕事で著名な企業と関わる機会が何度かあった。

そういうときに嬉しいのは、有名なドメインのサブドメインで、自分たちの作ったものを動かしてよいと言ってもらえる瞬間だ。

たとえば hoge.example.com のようなURL。

その example.com がよく知られたドメインであればあるほど、そこに自分の作ったものが置かれることに特別な感じがある。

ブランドを少しだけ背負うことを許されたような気分になる。

もちろん、それが成功を意味するわけではない。けれど、運よくそういう機会を得たとき、僕はドメイン設定の画面をしみじみ眺めてしまう。

URLが好き

たぶん、この感覚はそこまで特殊なものではない。

良いドメインには価値がつくし、ドメインはブランドや歴史と結びつく。

Bluesky にはドメイン認証があるし、イーロン・マスクは x.com というドメインのためにサービス名まで変えてしまった。

手慣れたインターネットユーザーなら、検索窓にドメイン名の頭文字を打ち込んでサジェストを起動する、あの感覚もわかるだろう。

何度もそこへ向かった記憶や感情が、一つのURLに結びついている気がする。

Box Maison でやりたかったこと

ここまで説明したところで、妻が大笑いした。

いろいろ話してくれたけど、要するに大好きなURLを並べて、そこに飛び込めるようにしたかったってことでしょ。そういう発想があまりにもあなたらしくて面白い

まったくその通りだと思った。

Box Maison についてはいくつか記事を書いたけれど、正直、自分でも何がしたかったのかうまく説明できていなかった。

でも妻のひと言で腑に落ちた。

僕がやりたかったのは、大好きなURLに物理的な存在感を与えて、それを自分の部屋にコレクションすることだった。

そして、必要なときにはそこへダイブできるようにすることだった。

Box-Maison ―― 「庭の話」に対する応答

僕は僕の作った Box-Maison を、新聞を見るような感覚でほとんど毎日使っている。僕の普段の仕事は「誰かが欲しい・必要とするものを作ること」であって、自分で自分の作ったものを自分のために使い続けるというのは、これまであまりないことだった。今後もBox-Maisonには色々と機能を追加してみたいとは思いつつも、自分の作ったものに対して、ひとまずはある程度満足している。

このアプリケーションを作るにあたってどのような世界観を表現しようと思ったのかは前回の記事に書いた。

sasau.hatenablog.com

この記事の中で影響を受けた物として「Among Us」「4次元マンション」「レディプレイヤー1」を挙げていたんだけど、これらはどちらかといえばプロダクトの骨格、仕様面やUXに関して影響を受けたもので、最後に思想的な部分として宇野常寛氏の「庭の話」については別枠で触れておきたいと思っていた。

www.kodansha.co.jp

はじめに断っておくとBox-Maisonはこの本における「庭」をこの世界に実現した姿であるとかおこがましいことを書きたいわけではないし、またこの本を背景にSNSに対するオルタナティブとして戦争を仕掛けたいというわけでもない。ないんだけど、Box-Maisonを開発しているときにちょうど刊行された「庭の話」は、気がつくとこのプロダクトの参照点になっていたのもあって、設計思想、あくまで僕の中の世界観としてこの本に共鳴しているところ、上手くいっているかどうかは別にして、底の方に流れるやりたかったことについては書き残しておきたいと思っていた。

現状認識

Xをはじめとした現在のSNSでは、ユーザーのプロフィールページへ行くと、そのユーザーのタイムラインが目に入る。
すなわち「ユーザー=タイムライン」であり、Xの解釈によれば「人とは何か、それは連続した発言だ」ということになる。
今のSNSの姿は、どちらかといえばWeb2.0時点における技術的な制約によるものであるところは大きい。とはいえ人々の歴史においても言論ほどレバレッジされてきたものは存在しないので、そういった歴史解釈の系譜の延長にあるようにも感じられる。
けれども、それだけだとあまりにも荒涼とした解釈ではないか?
実際の僕らは普段、家の壁の内側で本や寝具や生活家電や車と接して生きているわけで、SNSが世界を席巻した昨今、顔のついたタイムライン、すなわち剥き出しの言論のみと接する世界は、それはそれで不自然というか、あまりにも逃げ場がないように感じられて、そこが一つ、僕にとって打破したい「現状」だった。

人ではなく事物と向き合う空間

上記の現状認識は「庭の話」の問題意識と大枠の部分で共通するように思う。「庭の話」では繰り返しSNSプラットフォームは人間間を直接繋ぐもの、であるが故の様々な弊害を指摘しているからだ。「庭」というのはそこからの回避先としての場、またその条件を提唱している。

「庭の話」における「庭」の特徴として最も重要な要素は、「人ではなく事物と孤独に向き合う空間」であるとされている。ただし、庭は100%私的な空間ではなく、半分は公的なものに開かれている。もちろん「庭」というのは必ずしもSaaSとしての実装に依拠するものではない。既存のプラットフォームであるXやYoutubeであっても「庭」の条件を満たした使い方をする人はいるだろう。
とはいえ、Box-Maisonの空間設計はその「庭」的なものに対する応答となっている——つもりでいる。それは以下のような仕様からだ。

正方形のアバター

Box-Maisonでは、ユーザーは人型ではなく正方形のアバターとして空間に存在する。顔も表情もなく、ただ色と動きだけが個性を表す。これは意図的な選択だった。

従来のSNSやメタバース空間では、人型のアバターが主流だ。しかし人型であるがゆえに、外見・性別・年齢といった属性が暗に要求され、「人間らしさ」の演出が必要になる。Box-Maisonの正方形は、そうした社会的なコードから距離を取るための装置でもある。

正方形として存在することで、ユーザーは「誰であるか」ではなく「何をしているか」「どこにいるか」という事実だけで関係性を築くことができる。

床に配置されるURL

Box-Maisonの最大の特徴は、床にURLを配置できることだ。URLを設置すると、サービスが自動的にそのページのスクリーンショットを取得し、床に表示する。これは日次で更新されるようにもできるため、ニュースサイトや天気予報のURLを配置すれば、毎日変化する景色を作ることができる。

この機能が面白いのは、通常は「リンクをクリックして開く」という一瞬の行為でしかないURLに、物理的な存在感を与えている点だ。URLは床という場所を占有し、そこに近づくことで初めて内容が見える。正方形のアバターでその上を実際に歩くことで、URLは単なるハイパーリンクではなく、触れることのできる事物になる。

近年はSNSのタイムラインを通して、記事のURLが直接やりとりされることにより、メディアのトップページはスキップされる傾向が強くなっているように思う。しかしながら、自分の部屋に好きなメディアを並べ、日々の変化を自分なりに捉えること。SNSからではなく、トップページを通して自分で読む記事を選ぶという行為には、「庭の話」における孤独に事物とコミュニケーションを行うことと響き合う部分があるように思う。

まとめ

もちろん思想的な部分と実践には常に落差があるもので、あらゆる場所が「庭」、not「庭」の中間にあり、Box-Maisonも例外ではない。いろいろ書いたけれども、Box-Maisonは僕しか使わない機能の乏しい2Dメタバースもどきに過ぎないのであって、僕にとって起きている変化は日々のネットサーフィンの入り口が少しだけ変わっただけだ。

それでも、その「少し変わった」ということが、僕にとっては意味がある。「庭の話」や影響を受けたものがなければこのUXを存在させることはできなかった、という感覚は否定しようもなく存在する。

自分で作ったものを、自分のために使い続けている。その事実が、思想と実践の落差を埋めるわけではないけれど、少なくとも僕にとっては、剥き出しの言論だけではない、別の場所を作ることができたのだと思う。

P.S.

ちなみに、Box-Maisonには一定時間その場に滞在すると花を残せる機能がある。これは100%「庭の話」の影響を受けて実装したものだ。

「庭の話」における「庭」は園芸の話ではない。けれども、この本へのリスペクトを込めて、あえてサイバー空間に花を置いてみたくなった。数時間で消えてしまうほとんど無意味なものなんだけど、可愛いものになったんじゃないかなと思う。

シェアしたURLの上を歩くことができるオンライン空間

を作った。
名前は「Box-Maison」。

作ったもの・機能

作ったものは以下のURLで体験できる。
https://bmaison.cc/👤sasau

アバターについて

URLにアクセスすると、画面中央に↓が表示されるが、これがユーザーのアバターとなる。
デフォルトアバター

黒が味気ないと思ったら↓のボタンを押すとカラーピッカーが表示されるので、色を変えることができる。 色変更ボタンに矢印

基本の操作

アバターは画面をクリック、方向キーやWASDでアバターが移動する。

streamable.com

そのほかには、画面右側のボタンを押すと、絵文字リアクションやアバターが体を揺すってリアクションを取ったり、その場に花を残すことができる。
設置した花は数時間で消える。

streamable.com また、部屋の床にはURLポータルが設置できる(この部屋は僕の部屋なので他の人はURLを置いたりはできない)。
近づくとサーバーが取得したスクリーンショットが表示され、そこからURLを開くことができる。
このスクリーンショットは定期的に再取得するようにすることもできる。

streamable.com

その他

今回は説明を省かせていただくが、パスキーを使ってログインすると自分の部屋を作って他のユーザーを招待したり、入室に際してパスワードを設定することができる(まだベータ版なのであしからず)。

やりたかったこと・モチーフにしたもの

今回のアプリは、機能を紹介しただけだと何がやりたかったのかほぼ伝わらないだろうと思う。
なので、少々重いんだけど、いくつかモチーフにしたものを通して表現したかったことを書いていく。

『Among Us』

store.steampowered.com

Among Usはクルーの中からインポスターを見つけ出すパーティゲーム。ここから「見下ろし型のマップ」と「限定されたコミュニケーション手段」といった要素を拝借している。
Among Usでは、タスクやっているパートでは行き交うクルーと自由にコミュニケーションを取ることができない。そこに殺害の恐怖が混じることにより他者の行動に対する疑心が生まれ奥深い面白さに繋がっている。
Box-Maisonでは、「同じ空間にいる他者」に感じる圧迫感の軽減を目的として、コミュニケーションの手段を限定し、可能な限りポジティブなリアクションのみとしている。
裏のテーマとして、ああいったオンラインゲームの遊び相手と待ち合わせるのに役立てられたら面白いなというのもある。

『4次元マンション(ハイドアンドシーク)』

HUNTER×HUNTER カラー版23巻・173ページ

HUNTER×HUNTERに登場する念能力者ノヴの4次元マンションから「完全に独立している」「どこへでも繋ぐことができる無機質な空間」といった要素を拝借している。
無機質な部屋になったのは、どちらかといえば僕のデザイン能力の方に原因があるわけだけど、そこから全体的に四角い箱のようなデザインの空間になったりした。
ちなみに名前のBox-Maison(メゾン)は日本語としてはマンションにしたかったんだけど、英語だとプールがあるような「大邸宅」の意味になってしまうらしく、純粋に「家」の概念を持っていて、日本語でもよく目にするメゾンを使うことにした。

『レディプレイヤー1』

filmarks.com

Box-Maisonはやっていることとしては、いわゆる「2Dメタバース」に類するプロダクトと分類されると思う。
そういう意味では仮想空間を扱った作品に影響を受けているわけだけど、その中でもレディプレイヤー1に登場するVRプラットフォームのOASISに興味を持っていた。
レディプレイヤー1の世界では、我々がスマホに没頭するように人々はOASISに没頭していて、様々な情報、リソースにOASISを通してアクセスをする。
この様子を見ていて、汎用的に情報を扱えるという点でOASISはどちらかといえばブラウザみたいなものであり、あれは集団でブラウジングをしているんだなあという感想を持っていた。
それから少しして、Facebook社がMeta社になり、メタバースという言葉がバズワード的に広まっていったんだけど、既存のウェブをメタ的に扱う空間を表現してみたいなあという思いが強くなっていった。
最初はiframeでやろうと思ったんだけどもちろん現実的ではないので、個人情報を含まない形でサーバーが代わりにスクリーンショットを生成するような実装となっている。

自分で使ってみて

https://bmaison.cc/📰sasauやhttps://bmaison.cc/🎮sasauの部屋で、定期的に情報を仕入れたいウェブサイトを並べて巡回するのに使っている。
ブックマークでいいじゃんと思われるかもしれないが、やっぱり人間は日々変わる景色が重要であるようにも思う。
また、たとえばハッカーニュースに上がっている記事とGitHub Trendに上がっているリポジトリが重複した場合などはオッとなって優先度が上がるなど、響き合う時があったりして楽しかったりする。
最近はSNS等のタイムラインから流れてくる個別の記事に直接飛んでしまうため、メディアや公式サイトのトップページを見ることは減っているようにも感じているが、トップページにあるのは、実際は手間暇かけてデザインされた情報であって再評価されていいと思うし、これらを並べて吟味するのはなかなか新鮮な趣がある。
......これは新しいというよりは昔からある「新聞」の体験に近いのではないかと思うけれども。
自分が使うのにまだいくつか必要としている機能、やってみたい表現があるので、まだしばらくは細々と開発を継続したいと思っている。

おわりに

なんでも実況E(エッジ)板の開発から2年が経過した。

そういえば、ここのブログのタイトル「作ったもので2年に一回くらいの更新を目指す」は小説家の恩田陸さんによるエッセイ集『小説以外』から影響を受けている。
この本のまえがきには、彼女はエッセイの執筆が苦手で、「できれば、作者のデータなど何も残らず、小説だけが残っていくというのが私の理想なので、ひたすら小説ばかり書いてきた。」とある。そこからこの本のタイトルは決まったのだという。
僕も若い頃に、プログラマーになったのだからブログくらい作っておくかと考えたのだけど、人に見せられるものがあるとすれば作ったものくらいしかないだろうと思ってこのタイトルを付けることにしたのを覚えている。
これからもまた、そんなふうに自分の作ったものを書くことが出来たらいいなと思う。

使用した技術

本アプリで主に使用している技術は、https://bmaison.cc/@tech-galleryの提供でお送りしました!

匿名掲示板を作ってたらDDoS攻撃が来たのでCloudflare片手に戦ってた--2023晩夏

イントロダクション

前回の記事の通りで、趣味で専ブラに対応した掲示板を作っていた。 sasau.hatenablog.com

作ったものの、あまり宣伝や運営をする意欲もなかったので過疎掲示板としての時間が流れていた...。
というところまでが前回までの話だった。

その後、攻撃を受けるなどして、最終的になんでも実況Edge板は閉鎖してしまう...。のだけど、攻防の経緯をログとして残してあったので、この時どんなことを考えていたかも含めて、お伝えしておきたいと思う。

攻撃を振り返る

8/26

最初は8月26日に関連する掲示板に大規模な攻撃が来たことから始まる。

DDoS攻撃の経緯8/26

8月26日は、プロ野球のシーズン中で通常の試合が行われていた。野球に限らずだけど、実況民は実況中は本当に試合中のような行動をする。すなわち、実況中に何らかの要因でその会場が喪われると、安定した場所を求めて凄まじい勢いの大移動が起きる。エッジ板もそうした避難所の頭数となっていて、白羽の矢が立ったのが事の発端だった。余談だけどここで触れている防弾なんGは、その後も継続的なDDoS攻撃の標的にされてしまい、閉鎖の憂き目にあうことになる。

8/27~28

DDoS攻撃の経緯8/27~28

ここでDDoS攻撃を初めて経験した。エッジ板はCloudflareでのみ動いているため、そこまで遅くはならないだろうと高を括っていたのだが、明らかに読み書きのレスポンスが遅くなる。そのため、WAFを使ったIPアドレスベースのブロックを行うことになった。
CloudflareのWAFは無料でも超有能で、デフォルトでアクセスログにIPアドレスの所有元を示すASNまで提供してくれる。なので、これを検索することでリクエスト元を調べることができた。明らかに大量のリクエストを送ってくるASNはMiku Network Limitedやowl limitedなど、聞いた事のない海外のVPS事業者からだった。

8/27のアクセスグラフ

↑が8/27のアクセス量のグラフで、すこし分かりづらいけど、突然55k/hくらいのアクセスが発生していた。

8/29~30

DDoS攻撃の経緯8/29~30

8/29から本当の意味でのDDoS攻撃が行われることになる。この時点では攻撃リクエストには↑に書いているようにリファラに特徴があり、それによって大量の攻撃が一般回線から来ていることが浮き彫りになった。
このような攻撃はマルウェアに感染したゾンビPCにより構成されるボットネットから来ている可能性が高く、詳しくはわからないが、とある場所ではこのようなネットワークを時間貸ししているのだと言われる。
30日に実施したリファラによるブロックは功を奏し、一時的な平穏を得る。

9/3

DDoS攻撃の経緯9/3

少し空いて9/3、無意味な投稿をしてスレッドを埋めることを企図したスクリプトが発生した。これは本家5chでは日常的に発生しているため、予測したことであった。今後は書き込み前に

エッジ板認証画面

↑のように認証をしてもらうことでスクリプトを弾くことを目論んだ。
しかし...。

9/5

DDoS攻撃の経緯9/5

9/5、認証はhttps://d1ch.cc/authから書き込み時に返している6文字の英数の入力をすれば誰でもできてしまうので、なんらかの代行サービスを使ったのか簡単に突破されてしまった。
今思うとこの辺りでスクリプトに対して手動で対応していたのは疲弊の原因になった気もする...。

攻撃に関してのまとめ

  • DDoS攻撃は脅威だが、リファラを使ったブロックがある程度功を奏していた、ただしこれは一時しのぎであることは明らかだった
  • 認証は代行などを使えば認証済みのCookieを大量に抱えられてしまう問題があった
  • 認証処理自体にもパフォーマンス上の問題を抱えていた

以上のような課題があり、パフォーマンス改善と、認証の脆弱性を塞ぐための改修を行うことにした。

9/9改修

9/9の朝、改修を行なった。それに伴いDBは作り直しになり、まっさらなところからの再出発となった。更新作業は滞りなく完了することができた。

9/9改修の内容

技術的な詳細は↑の通り。
まず、DBにCookieテーブルなどがあって、これに認証フラグ等を持たせていたのだが、これが現状では明確に遅かったのでCloudflareのキーバリューであるCloudflare KVを使うことにした。Cloudflare KVはなかなかすごいというか、RedisのようなシンプルなAPIであるが、Redisのようには動かない。具体的にいうと、書き込んだあと、それが全てのノードに伝播するまで最大1分かかる。しかしReadは限りなく無限にスケールする。後述するけど、パフォーマンスとしてはこれが多大な効果があった。

また、喫緊の認証の問題は、専ブラでのリクエストに使われたIPアドレスと認証リクエストに使われたIPアドレスが一致することを要求し、また認証の操作を5分以内に行ってもらうことで対応した。

他には、専ブラは書き込み内容を特定の形式にして、それをShift-jisにしたものしか受け付けないのだけど、この処理を毎回GETリクエストが来た時に行うのはかなり遅いような気がした。そこで、d1のSQLiteにblob型のカラムを用意し、Shift-jisにしたものをそこに入れてしまって、書き込みを返すときは単なるバイト列のconcatだけで済むようにといった地味な最適化も行った。

改修の成果

まず、認証の脆弱性を突いた攻撃は発生しなくなった。これは予想外の結果だった。認証代行を使うのは難しくなったとはいえ、まだ手動で認証を済ませたCookieを作り出すことは可能であり、嫌がらせに使ってくるかなと思ったため。労力に見合わないと判断したのか、何か使いたくない事情があるのかなと考えていた。
しかし、IPアドレスの一致を要求することは、いくつかの技術的な問題を誘発した。専ブラによっては常にIPv4で通信しているものがあり、その場合ブラウザがIPv6を使って通信してしまうと何度やってもIPアドレスが一致しない。このような場合はユーザー側がどうにか対応する必要があるなど、大小の問題が発生した。

次にパフォーマンスについて。改修前はどうしても60k/hくらいで遅くなり始め、80k/hくらいからエラーが出始めるような感覚だった。

改修前8/28のリクエストログ

改修後は、200k/hを超えてもほぼ負荷を感じないレベルまでパフォーマンスの改善が見られた。この日はタイガースのアレ前夜。

改修後の9/13のアクセスログ

この日はタイガースの「アレ」の日。

タイガースアレ時のログ

ここまでいくと、DDoS攻撃なのか、純粋な住民の熱狂なのか区別はつかないが、少なくとも読み込みにおいて「重い」と感じることは減っていった。
負荷が集まると3分程度500エラーが発生してしまうことはあり、完全な無停止とは行かないものの、掲示板としては及第点を取ることはできていたように感じる。 ただ、パフォーマンスとのトレードオフとはいえ、Cloudflare KVの最大1分の伝播時間の問題は地味にご不便をおかけしたように思う。認証したのに通らなくて不快に思われたことがあれば申し訳なく思う。

その後、閉鎖まで

技術的には諸々克服の兆しが見えていたのだが、精神的にはかなり疲弊していた。
特にDDoS攻撃は可視化された悪意の数字であり、Cloudflareのダッシュボードにログインしてこの数値を見るたびに心身の健康が少しずつ削られていった。

これはある日のアクセスログであり、どちらかといえば脅かしに近いのだが4M/1hのリクエストが送られてきていた。これもダメージが大きく、この状況では継続が困難であると判断した。10月1日に閉鎖の告知を行い、なんでも実況Edge板を閉じさせていただくことにした。
叩かれると思っていたのだが、告知のスレッドでは暖かい言葉を掛けていただき、本当にありがたかった。

なんでも実況Edge板まとめ

書き込み数: 1,426,894
スレッド数: 18,187

実況的にあったこと

掛かった費用について

↑が実況的にはかなり回転していた9月の領収書。元々払っていたCloudflare Workersの5$プランを入れても費用は18$程度で収まっている。 ただし、d1はα版を使用していたためか請求に含まれていない。しかし、これがWorkersと同程度の金額だとしても30$弱であり、あり得ないほどの盛況でも50$程度を見ればある程度運営できるのではないか、と思う。

感想

結果的には色々なものに負けてしまい、申し訳ない気持ちと残念な気持ち、またたった一人でしか事に当たることができなかった自分の限界などを感じる。しかしながら、エッジコンピューティングを使ったアプローチでDDoSやスクリプト攻撃に対しても一定の可能性を示すことはできたかなと思っており、そこに関しては成果かなと思う。
短い間だけど自分のコードで戦うような「エッジランナー」の心持ちでいられたことはなんだか幸せなことだなと思っていた。

使っていただいた方へ

住民の方には、もっと早く感謝や、多少でも有益な情報を届けたい思いはあったのだけど、閉鎖の判断を下すのは自分の中でとても辛く、苦い思い出になってしまっており、様々な思いを無視する形で気がつけば今年も最後の日になってしまっていました。
今日までその後の情報は入れることが出来ていませんでしたが、現在ではこの技術スタックを用いた避難所開発のコミュニティができつつあるようで、救われた思いになりました。もちろん自分はその成果を何ら主張する立場にはないですが、それらの活動に最大限の感謝とリスペクトを送らせてください。ありがとうございます。ここでの情報が何かお役に立てば幸いです。