今年もこの季節がやってきました。
2023 から参加しているので、今年で 4 回目。
試合中にログを残しただけの参加記です。 殴り書きです。
Day -2 まで
何もしてない。よくない。
しかも最近は積極的に Web 触っているわけではないので、さらに良くない。
今年はみんな AI 丸投げになるんだろうな~と勝手に思っている。
競プロ脳なので AI 丸投げとかマジでおもんないと思ってますからね、もし上位が AI 丸投げしてたら来年以降もう出ません。 (出ます)
Day -1 (3/19)
環境構築
ずいぶん前に運営のうちの 1 人 Sor4chi に「Windows で動作確認してね!」と言ったものの、どうせ 「Windows 使いとかいう開発者の少数派が悪いんや!」とか言ってきそうだし実際それに近しいこと言われたので、 GCE の c4d-standard-4 (vCPUs: 4, RAM: 15 GiB) で Ubuntu 24.04 LTS を立てて環境構築をする。
料金計算ツールだと 48h, ディスク 100 GB でだいたい 2,000 円。
買い替えるより安いので許容。
まあ macOS のほうがいいんだろうが、 AWS で Dedicated Host 立てて... とかめんどくさいし高いので妥協。立てたとしても macOS に慣れてないので使いづらくて無理すぎる
SSH でアクセスし、リモートデスクトップ環境を整える。
使用するのは Chrome リモートデスクトップ (remotedesktop.google.com)。
楽にセットアップできて良い。
sudo apt install ubuntu-desktop wget https://dl.google.com/linux/direct/chrome-remote-desktop_current_amd64.deb sudo apt install ./chrome-remote-desktop_current_amd64.deb DISPLAY= /opt/google/chrome-remote-desktop/start-host --code="REDACTED" --redirect-url="https://remotedesktop.google.com/_/oauthredirect" --name=$(hostname) sudo timedatectl set-timezone Asia/Tokyo sudo passwd $USER sudo passwd ubuntu sudo apt install fonts-noto-cjk
日本語入力ができないので、 Google Fonts から Noto (Sans|Serif) Japanese をダウンロードしインストール、 mozc も設定から入れて再起動。
keymap も更新し準備 OK。
- Google Chrome
- VS Code
- fnm
- gh
- Node.js v24.14.0 (LTS)
- npm
- pnpm
- @google/gemini-cli
- sqlite3
も入れておいたので、たぶん一通り大丈夫かな
Git の設定もした。
試しに speed.cloudflare.comで速度計測してみたら、 Download で >1 Gbps 出て大爆笑 w
さすが Google のデータセンターなだけある。
WebGL サポートしてないらしいが、今回のお題は SNS サイトということでどうせ使わなさそうだしいいか。
困ったらローカルからアクセスしてやることにする。

対策リスト?
去年までやることリスト作っておいたけど、別にそんなに使わなかったので最低限のまとめ:
- Chrome 最新版は
146(https://chromiumdash.appspot.com/schedule) - dev → prod
- DevTools のネットワークタブを見る
- HTTP/2 or 3 にする
- 圧縮が有効になってるか確認
- 最近 Temporal が Stage 4 になって Chrome 144 から組み込まれたらしい。 時間回りの polyfill とか消せそう
- SNS でありがちな無限スクロールとかやってそう。
react-infinite-scroll-component(https://www.npmjs.com/package/react-infinite-scroll-component) とか使えそう。 - メールアドレスとパスワードの ReDoS をチェックする。 https://devina.io/redos-checker
- ちゃんと食べて寝よう!
ざっとコード読んだら設定周りは GitHub Copilot Agents に丸投げしてもいい気がするな
競プロ脳なので AI 丸投げとかマジでおもんないと思ってますからね
🤔
ひとまず Private で wsh2026-workrepo を作成しておいて、 Issue で「Bundler を Vite にする」「ReDoS 修正」を作成しておいた。
(現在は wsh2026-workrepo を rename して a01sa01to/web-speed-hackathon-2026 になりました)
Day 1 (3/20) - 開始前
以下のメッセージが来ていたことに気づく。
Sor4chi (3/19 22:50)
Windowsの動作確認したぞ
ありがてぇ~けどごめんねぇ...
10:02 ごろ、 GitHub Status に Copilot Coding Agent Sessions が落ちてそうな通知が流れていた。 いい流れ...?
10:28 "We are rolling out our mitigation and are seeing recovery." あちゃー。 (??)
ところでなんかネットワークが不安定なんですが、困ります
Day 1 - 競技
CyberAgentHack/web-speed-hackathon-2026 を全力でリロード。
git clone https://github.com/CyberAgentHack/web-speed-hackathon-2026.git cd web-speed-hackathon-2026 git remote set-url origin https://github.com/a01sa01to/wsh2026-workrepo.git git push -u origin main
koyeb にデプロイし、準備 OK
開始
mise!? なんですかそれは
今年は採点 web-speed-hackathon-2026-scoring-tool じゃないんだ、ひとまず参加登録一番乗りした ( Issue #2 CyberAgentHack/web-speed-hackathon-2026 )
だが採点の Actions が Skipped とのこと。 動いてますか...?
10:47 直ったとのことで retry すると動いてそう。
227.40 / 1150.00 点。 CLS がほぼ満点... まあ最初はそうだよね
コードを読む
ざっとコードを読んで、怪しいところを見つけにかかる
気づいたら Issue を立てておくことに。
ReDoS
client/src/auth/validation.ts の /^(?:[^\P{Letter}&&\P{Number}]*){16,}$/ が ReDoS になってる。
「『文字ではない かつ 数字ではない』ではない が 16 回以上」なので、「文字 または 数字 の 16 回以上」と等価で、 /^[\p{Letter}\p{Number}]{16,}$/ にすれば OK (後で気づくが ([...]*){16,} なので大嘘)
client/src/search/services.ts の /\b(from|until)\s*:?\s*(\d{4}-\d{2}-\d{2})\d*/gi
Gemini に投げると /\b(from|until)\s*(?::\s*)?(\d{4}-\d{2}-\d{2})\d*/gi を返してくれた。
同ファイルの /since:((\d|\d\d|\d\d\d\d-\d\d-\d\d)+)+$/ も ReDoS になってるっぽい。/since:((?:\d{4}-\d{2}-\d{2}|\d)+)$/ で OK。
/since:.*(\d{4}-\d{2}-\d{2}).*/g ここもだめ。
なんですか? 今回 ReDoS 祭りですか?
というか server/src/utils/parse_search_query.ts にほぼ同じ処理あるじゃん。
ここ最初に見ておけばよかった...
念の為 2の18乗 回、最下部かどうかを確認する
草
Vite 置換 (できず)
Webpack 使ってたので Copilot Agents に丸投げしておいた。
11:00 くらいに完了したのでローカルで動いてるかテスト。
E2E のスナップショットが撮れてなかったので、最初に回しておくべきだった。
開いてみると、壊れている。 壊れてるよと言って丸投げする。
使いすぎて Rate Limit に引っかかってしまった。そんなぁ
Gemini に投げても全然動いてくれないので後回し。
Production 化
とりあえず Webpack でやることに。もう 11:57。
webpack-bundle-analyzer を入れ、解析していく。

すべての optimization 設定が false になっていたので true にする。
PR #9 a01sa01to/web-speed-hackathon-2026Aspect ratio
これは css で aspect-ratio: width/height と書き換えてやればいいですね
CaX が重すぎて RAM を食い尽くし、リモートデスクトップが切断される事態が頻発。困るよ~
Covered Image
これは object-fit: cover でいい気がするな
いや、なんか表示がずれてる...
無駄なクラスがついていた + height 指定がなかっただけだった
Alt を exif から読み出すとかいうよくわからんことしてるので直したいな~ Issue (#11) にあげといていったん後回し
2^18
明らかに無駄なので、 Intersection Observer 使って書き直すことに。
一番下に表示される div を作ってやる。 1px くらい追加されちゃってもいいよね...?
重すぎてまた切断されてしまった。勘弁してよ~
swap ファイルを 8 GiB 追加したので、多少軽減されたはず...?
jQuery
jQuery 消したいよね
You Might Not Need jQuery を使って書き直し。
$.ajax だけしか使ってなかったのでこのサイトそんなに使う必要なかった。
Lodash
lodash もいつも消してるのでやる。
Vite 置換 (できた)
さすがに bundle size がでかすぎるのでどうにかしたい。
どうにかして server に逃がせないかな~
いったんチャンク分割かな
Gemini と会話しながら修正していく
やっぱり Vite にしたい気持ちが出てきたので、やってみる。
行けた!!!!
もう 14:10
~~もぐもぐタイム~~
lazy 化
最初に一括で重いのが読み込まれちゃうので lazy import する
ついでに createRoot が load 後にされていたので、 DOMContentLoaded 後にするように変更。
リンクが a タグの遷移してるな?と思ったので react-router の Link に変更。
したが、なぜか click イベントハンドラが登録されてなくて謎。
react-router v7.9.4 だと unstable_useTransitions すらまだ対応してないらしい。
上げても動かず、よくわからなかったのでいったん Link に書き換えるだけして飛ばし。
ここでこんなメッセージが届く。
【運営からのお知らせ】
GitHub Actions の runner の並列上限に引っかかりデプロイ、及び計測の滞留が発生しております
www
Koyeb にしといてよかった。
自分で用意すると課金は必要だけど詳細設定できてうれしいし、こんな事態が (余程のことがない限り) 起きないのもいい。
Vite fix
ここでデプロイされているサイトにアクセスしてみると Cannot GET / と出ている。困った。
Rollback しながらテストしていく。
すると、 Vite のコミットで落ちてることがわかる。
出力先が違ったみたい。
HTTP/2 (できず)
やっぱり HTTP/2 にしたいので、する。
Gemini に聞いたところ、 Express でやるのはめんどくさいので Fastify に移行したほうが早いかも?とのことでそうする。
歯を磨きながらなので Copilot Agents に投げる。 PR #22 a01sa01to/web-speed-hackathon-2026
PR を待っている間にひとまず Connection: close になっていたので keep-alive にしてデプロイ。
ReDoS 修正
Server と合わせるだけで簡単なのでここをやっちゃう。
PR #23 a01sa01to/web-speed-hackathon-2026終わったところでさっきの HTTP/2 の Agent が終わったみたい。
がダメダメ。マジで使い物にならない。
いったんスキップ。
FontAwesome
なんか 1 ファイルの svg としてまとまってるんですが?
使ってるのは
solid: home, search, envelope, edit, user, sign-in-alt, balance-scale, arrow-right, arrow-down, paper-plane, exclamation-circle, pause, play, circle-notch, images, music, video regular: calendar-alt
だけなので、それ以外のやつを消してやる。
PR #24 a01sa01to/web-speed-hackathon-2026ffmpeg, imagemagick
こいつら重すぎるので server に処理を任せたいな~と開始時から思って早 6 時間 (16:48)
動画周りの処理がよくわからないので、適当な動画をアップロードしてみる。
すると、最初の 5s しかいらないらしい。
server 側に処理任せちゃっていいな?
[17:40] 動画と音声は完全にできた。気がする。
あとは image なんだよな~
Exif から読み出すのは piexifjs が比較的軽いから現状妥協かな~~
これ imagemagick 消すだけで完結するのでは?
CSS 修正
いったん VRT を回してみる。
なんか一部の CSS が読み込まれてないような diff が表示されている。
normalize.css が読み込まれてなさそう。
それはまあめんどくさいので normalize.css を index.css にべた書きすることで対応... (よくない)
あれ?いつのまにか Link がちゃんと動いてる????なんだったんだ
なんか日付が Jan 1, 2026 形式になってる?そこ変えた覚えないんだけどな
意味が分からん!
Translator
これも Server に回したいよな
ただ @mlc-ai/web-llm は express でホストできないっぽいので、 Cloudflare Workers AI を立てて叩くようにしてみる。
画像・動画の軽量化
ほかの部分で忙しくて全然できてなかったや。 max で横幅 640px なので、縮小しておく。
profile image は 128px でよさそう。
こんな感じに書いた。
import fs from "fs/promises" import path from "path" import sharp from "sharp" import { PUBLIC_PATH } from "./paths" export const tmp_sharpResize = async () => { // 画像をリストアップ const files = await fs.readdir(path.resolve(PUBLIC_PATH, "images", "profiles")); console.log(path.resolve(PUBLIC_PATH, "images")) // 画像をリサイズして保存 await Promise.all( files.map(async (file) => { if (!file.endsWith(".jpg")) return; const inputPath = path.resolve(PUBLIC_PATH, "images", "profiles", file); const outputPath = path.resolve(PUBLIC_PATH, "images", "profiles", "resized", file); // 画像をリサイズして保存 await sharp(inputPath) .resize(128, 128, { fit: 'cover' }).jpeg({ quality: 80 }) .toFile(outputPath); }) ); }
gif 変換は Gemini に投げて書いてもらった。
#!/bin/bash # 保存先の専用ディレクトリ名 OUT_DIR="resized" # カレントディレクトリに出力先のフォルダを作成 mkdir -p "$OUT_DIR" # .gif ファイルを検索。ただし作成した $OUT_DIR の中は検索から除外する find . -type f -iname "*.gif" -not -path "./${OUT_DIR}/*" -print0 | while IFS= read -r -d '' file; do # 元のパスから先頭の "./" を取り除いて整理 clean_path="${file#./}" base=$(basename "$clean_path") # 出力先に元のディレクトリ構造を再現して作成 target_dir="${OUT_DIR}/" mkdir -p "$target_dir" # 出力ファイルのパス output="${target_dir}${base}" echo "処理中: ${file} -> ${output}" # -vf "scale=640:-1" で幅を640に固定、高さは縦横比を維持して自動調整 ffmpeg -y -loglevel error -i "$file" -vf "scale=640:-1" -t 5 -r 10 -an "$output" sleep 1 done echo "すべての処理が完了しました!ファイルは ${OUT_DIR}/ 内に保存されています。"
ローカルで試してみる。
その途中、 serve-static のオプションに etag, lastmodified があったことに気づく。
すかさず true にしておく。
見てみると、 Alt が表示されてない。
Exif が消されてしまった模様。
#!/bin/bash # ディレクトリのパス設定 SOURCE_DIR="./base-public/images" TARGET_DIR="./application/public/images" # exiftoolがインストールされているか確認 if ! command -v exiftool &> /dev/null; then echo "エラー: exiftool がインストールされていません。" echo "sudo apt install libimage-exiftool-perl を実行してインストールしてください。" exit 1 fi echo "EXIFデータのコピーを開始します..." # コピー元ディレクトリ内のすべての .jpg ファイルをループ処理 for src_file in "$SOURCE_DIR"/*.jpg; do # 該当するファイルがない場合の処理をスキップ [ -e "$src_file" ] || continue # ファイル名のみを抽出(例: image1.jpg) filename=$(basename "$src_file") target_file="$TARGET_DIR/$filename" # コピー先ディレクトリに同名のファイルが存在するか確認 if [ -f "$target_file" ]; then echo "$filename のEXIFデータをコピー中..." # -TagsFromFile でコピー元を指定 # -overwrite_original でバックアップファイル(.jpg_original)の生成を抑制 exiftool -TagsFromFile "$src_file" -overwrite_original "$target_file" else echo "スキップ: コピー先に $filename が見つかりません。" fi done echo "すべての処理が完了しました!"
問題なく動作することを確認。
PR #28 a01sa01to/web-speed-hackathon-2026そのうち CF R2 でファイルホストとかしたいな (なんならフロントは Workers からホストできそうだしワンチャン全部 CF に乗るのでは...)
フォントの Subset
全然対応してなかったや。
使ってる文字は以下だけ。しかも全部 bold。
利用規約 第1条(適用) 第2条(利用登録) 第3条(ユーザーIDおよびパスワードの管理) 第4条(利用料金および支払方法) 第5条(禁止事項) 第6条(本サービスの提供の停止等) 第7条(著作権) 第8条(利用制限および登録抹消) 第9条(退会) 第10条(保証の否認および免責事項) 第11条(サービス内容の変更等) 第12条(利用規約の変更) 第13条(個人情報の取扱い) 第14条(通知または連絡) 第15条(権利義務の譲渡の禁止) 第16条(準拠法・裁判管轄)
Ubuntu 側でのフォント変換は知らないので、 Windows ソフト (サブセットフォントメーカーと WOFFコンバータ) を使って woff2 にしていく。
PR #29 a01sa01to/web-speed-hackathon-2026ビルド終わったら計測回してもぐもぐタイム + おふろへ。 20:20。早いよ~
これまで全然計測回せてなくて 2 回目なのやばい。
21:25 帰還。
499.95 点。まずまずの結果。
入浴中にいくつか案おもいついたのでやってみる。
negaposi
negaposi のファイルが重いので server に移してやる。
PR #30 a01sa01to/web-speed-hackathon-2026Posts の Pagination
ネットワークタブを見てみると、 Posts 取得 API で大量に読みに行ってる。
最初から全部取得しに行ってるので、それは遅いよね...
コード読んでみたら server 側はすでに Limit, Offset の対応されていてうれしい!
音声・画像・映像の Placeholder
こいつら binary fetch で読み込んでるのでどうにかしたいが、画像は Exif 読み込む都合上できないし、音声は波形を読みださないといけないので困る。 映像も canvas に書いてるのでいったん困る。
結局何もできないやんけ!
ひとまず CLS 対策として loading の placeholder 処理だけ書いといた。
PR #32 a01sa01to/web-speed-hackathon-2026Tailwind を Vite で入れる
Tailwind が CDN 経由になっていて遅いので、 Vite plugin で読み込むことにする。
想像以上に簡単に組み込めてしまって逆に怖いので VRT 回すことに。
でもスタイルは問題なかった。
ついでに normalize.css をもとに戻しておいた。
DB 周りの修正
なんか E2E を回してみると、 DB が壊れていることが発覚...???????
変えた覚えありませんが?????
全然直らないので、いったんローカルのリポジトリを削除して再 Clone してみることに... が、直らず。
初期状態 (手つかずの web-speed-hackathon-2026 リポジトリ) だと動いているので、まあここまでやってきたことによる不具合なんだろうなぁと
Git bisect の出番や! 存在しか知らないので Gemini に聞きながら進めていく。
結果、 Posts を Pagination にした #31 がよくないことが判明。なぜ...?
Client 側しか変えてないのに????
あー!!! /search も infinite scroll の対象じゃん!!
え、でも初期状態でも /search に limit つけると落ちるんですが...
逆に offset つけてもいける。 なぜ...?
ひとまず limit をコメントアウトすることで対応。
momentjs 消し
そういえば時刻の表示もおかしかったな... ついでに直しちゃうか
bisect すると、 #18 の Vite 移植がよくないっぽい。うーん???
もう moment やめて dayjs に移植する。
いや、ここで Temporal, Intl の出番じゃないか?
試してみると Temporal の型が出ず、困った...
dayjs でいいや...
Index はる
全然張ってなかったので index を張ることに。
Copilot に任せていた感じで大体よさそうなのでマージ。
PR #34 a01sa01to/web-speed-hackathon-2026highlightjs の言語を必要なものだけにする
Crok の応答となる元の Markdown 見てみると、 json, mermaid, bash, python, ts, rust, sql, text しか使ってないが、全言語を読み込んでいそうなので絞る。react-syntax-highlighter の READMEを見ながら直していく。
Mermaid の言語ないんですが???
よくコードを見てみると、 javascript に fallback されている?
実際にレンダリングしてみると、なんか違う。react-syntax-highlighter とか依存先とかのコードを追っかけてみると、 highlightAuto で自動判別してるらしい。
めんどくさすぎる...
仕方がないので pnpm patch react-syntax-highlighter で console.log を挟んで取得する。
すると ebnf だということが判明する。 ほんとか?
見比べると良さそうなので、そのまま続行する。
Day 1 最後の採点
[25:24] 今日はひとまずここまでにして、寝る前に採点を回して結果をみておく。
[25:35] 530.30 点。 39 位。全然伸びないなぁ... おやすみなさーい
Day 2
[07:10] 目が覚めてしまう。眠い。全然疲労が取れてない感じがする。開始。
画像 Alt を DB から
やっぱり Exif から読むのやめよう!
最初から DB から空文字の Alt が読み出されているので、保存するようにしてやればよい。
ただこのままだと元からある投稿に Alt がつかないので、 initialize するときに付加してやることにする。
波形データを static に
こっちも static にしてやりたい。
まあテキストファイルで配置してやればいいか
Nodejs だと AudioContext がないので、 web-audio-api を入れて投稿時に取り出してやる。
こっちも元の投稿に波形データがないので、public ディレクトリ内であらかじめ生成。
PR #37 a01sa01to/web-speed-hackathon-2026デプロイで落ちる。 web-audio-api のビルドで Python が必要だと怒られる。
Commit 922a60b a01sa01to/web-speed-hackathon-2026 直した。
映像データ
これどうしようかな~と Gemini に聞くと、 mp4 に変換すれば?と言われる。
そのほうがファイルサイズも軽いし js で canvas 使わずに再生・一時停止ができる。 🦀
まあ劇的にファイルサイズ軽くなるわけでもなかったが、いろいろライブラリが消せるので OK。
kuromoji
こうすればあとは全部 Cloudflare R2 に逃がせないかな~と考えていると、 dicts がいる。
こいつ必要なのか?とみてみると、 ChatInput の kuromoji で使っている。
Tokenizer は Server に逃がしてやりたいな~
E2E を回すと検索結果がおかしかったので、 limit, offset を SQL クエリでは外して結果が一致するようにした。
パフォーマンス的にはよろしくないが、レギュ落ちしては元も子もないので妥協。
[09:13] このへんで採点回しながらもぐもぐタイムへ。
[09:32] 帰還。606.80 点。苦しい...
ただ CLS と TBT はいい感じになっているので、 FCP, LCP, SI をどうにかしたいな~というところ。
R2 から読み出し
やっぱり R2 に移動すれば結構よくなるんじゃないか? ということで実装。
initialize がめんどくさいが、まあ uuidv4 重複しないだろ!と妥協。
OK。 フォントとかそのほかのファイルも移動させちゃう。
svg は xlinkHref が unsafe とのことだったので、それぞれファイルを切り分けて対処。...する必要なくない?
もう bundle に含めちゃえばいいのでは
Crok も Figma に投げると多少圧縮されて返ってくるので、それを使ってやる。
これでどのくらい変わったのか計測。うーん、そこまで大きく変わってなさそう。なんなら少し下がってる。
CDN 有効活用
FCP, LCP, SI が遅い。 しかも Koyeb の CDN でキャッシュされてない。Cache-Control 設定を変えてなかったことに気づく...
API は一応 maxage=0, no-transform にしておいて、 static だけ public, max-age=3600 にしておいた。
これで再計測してみる。 多少 FCP, LCP, SI が上がった。
ほか少し下がったところがあった (特に Flow の Crok AI の INP がめっちゃ下がった) が、合計は 628.75 と微増。
DM 画面
いい加減 DM 詳細ページを頑張りたい。
そういえば setInterval(..., 10) があったな
これって conversation.messages.length が変わった時しか発火しなくていいよな
念のため 100 ms 待ってから scroll する
あと毎回 typing を発火しなくても 10s くらい、いや余裕をもって 5s ごとでいい気がするな
すべてを Worker に
リクエストをよく見ると、 R2 のリクエストに HTTP/3 が使われていない。
しかも Cache-Control がセットされてない。
探してみると Worker 経由じゃないと HTTP/3 にならない?とのこと。
https://community.cloudflare.com/t/will-cloudflare-r2-support-http-3-or-not/798982
ということで、いったんすべてを Workers から提供して、 API は Proxy してみることにした。
すべてにおいて HTTP/3 が使えるようになってうれしい!Cache-Control が public, max-age=0, must-revalidate になってるのはどうしようもないのか?_headers ファイルに書くといいらしい。
計測。
SPA 特有の 404 ハンドリングがミスってた。
直して再計測。 669.25 点。 サインインに失敗しているらしくて困る。
手元で workers.dev に接続してみると動くのになぜ...?
フォントのレンダリングブロック
PageSpeed Insights で見てみると、 CSS でレンダリングブロックされているみたい。
フォントに font-display: swap 指定し忘れてた w
あ、そういえば profile のヘッダがおかしいんだったな、修正
Commit 2657b76 a01sa01to/web-speed-hackathon-2026 Commit 612b154 a01sa01to/web-speed-hackathon-2026redux-form → react-hook-form
検索画面で何も入力しなくてもエラーが表示されない問題があった (正確には、表示されるときとされないときがある)
これ直さないとまずいよな~と思って Gemini とやり取りしていると、「WSH やってんの? redux-form は負債として残されてる可能性が高いよ」といわれたのでそのまま移行する流れに
途中、パスワードの正規表現が違うことに気づき、危ない...
「16 文字以上で記号を含まない」じゃなくて「0 文字以上で記号を含まない」なのか
うおー!!!できた!!!!
PR #45 a01sa01to/web-speed-hackathon-2026[14:57] ここで計測。あと 3 時間半。短い。
681.15 点。
採点落ちを直したい
「サインインに失敗しました」この計測がマジで謎。
ローカルで動作を見てみると、読み込みに時間がかかってるみたい。
ログアウト後に / に遷移して画像を一気に読み込もうとするのがダメみたい。
ログアウト後 / にリダイレクトしなくてもいいのかな?
レギュレーションには書いてないので clar を投げる。
Sor4chi が一瞬質問に「草」リアクションつけた後にスレッドが建てられ、 Sor4chi 宛に DM で質問するようにとのこと。
- ログアウト後
/にリダイレクトされる必要はあるか- Crok のレスポンスは 1 文字ずつ SSE で返す必要があるか (一気に送っても問題ないか)
以上お願いします
と投げると
- 既存実装の通りです
- いいよ
と返ってくる。そんなぁ
あ、ふつうに fetch で priority 高めにすればいいのか
ついでに img に loading=lazy, decoding=async を設定。
画像投稿も見てみる。
めちゃくちゃレスポンスが遅い。
Koyeb で見るとメモリ使いすぎてカツカツだったので、 RAM 4 GB に設定してリトライ。
(ついでに Actions に近そうなアメリカリージョンを選択した)
ふつうに自分で画像を投稿してみると、 R2 に画像があるのに読み込まれない。/images/... を images/... に変換する必要があり、 R2 へのリクエストが違っていた。
あと Alt 情報がちゃんと読み込めてないっぽい。
Gemini に投げると exifr を使えと言われたので、仰せのままにやる。
Crok のレスポンス
SSE で 1 文字ずつ送っているが、これをまとめて送ればだいぶ TBT 上がらないかな~
さっき質問して "いい" と返ってきたので、やる
100 文字くらい一気に送ろうかな
一気に全部送るのはダメだよね?
Asa
さっきの質問に関連して: さすがに Crok レスポンス一気に全部送るは許されませんよね...?Sor4chi
SSEを守ってくださいAsa
SSE 使って全部送る場合ってどうですかSor4chi
SSEを守ってくださいAsa
😥 逆にどのくらいなら許容されますか?言及不可です?Sor4chi
です 😭
そんなぁ
100 文字くらいにしとこ
ローカルで試してみると、全然上がらん。
よくよく見てみると、 Markdown render に key={content} がついている。
それはだめですねぇ
そのほか Gemini とやり取りして直しにかかった
Commit 6774501 a01sa01to/web-speed-hackathon-2026JS 経由 height
すっかり忘れてた。 ${Math.min(textarea.scrollHeight, 200)}px を max-height でやるやつ。
max-height でやるとうまくいかない。
Gemini に聞くと field-sizing: content; でできるらしい。
ここで計測してみる。 17:22 あと一時間や。モームリ
ただ上位勢のサイトを見てみると、レギュ落ちしてそうな雰囲気がある
685.70 点。全然上がらん。困ってしまった。
投稿時ファイルタイプチェック
やっぱり画像アップロードが遅いのが気になるよな~
そういえば「filetype が undefined じゃない」のチェックしてるけどさすがに要らなくないか?
不要依存関係削除
regenerator-runtime いらんな
pako ってなに? gzip 圧縮してそう。これいるのか?sendJSON でそんなに重いものを扱ってるわけではないので消しちゃお
圧縮といえば zlib とかあったな まあやらなくていいか
PR #50 a01sa01to/web-speed-hackathon-2026あと 40 分。ここでもう一回計測してみる。
696.60 点、ギリ 700 点に届かん。
Avif 化
まあいいやと放置した結果、すっかり忘れてた。
こうなることなら最初からやっておけばよかったね
計測を回した瞬間、 Koyeb にデプロイしてないことに気づく。
急いでデプロイしたが、結局タイムアウトになってしまった。
再計測すると 690.50 点。うーん...
迷走
もうこうなったらリモートのデスクトップを外部公開してやっちゃえばいいのでは?
いや結局通信距離が長いからローカルほど長くならんやろ
なんもわからん...
終了
690.50 点、暫定 42 位。学生 21 位。
黄~緑 色変 (690 点?) のボーダー...
マジで無理すぎる... 終わりや
やりたかった
- SSR (というかデータの inject) とかやりたかったなぁ
- sendFile を zlib とかで圧縮するとよかったんだろうか
- サーバーから送るデータ削減してないや
- もっとちゃんと対策すればよかったかねぇ
反省
- E2E 回しすぎ! 計測もっと回そう!
Coding Agents は使い物にならない丸投げする部分と自分で書く部分をちゃんと分けよう
感想部屋とか解説とかを見ての感想
- gif アニメーション、 WebM に変換する手か、完全に忘れてた...
- Web Translator API、そんなのあったね
- img の srcset 完全にさぼってた...
- Scheduler API なにそれ、使われていたことすら知らないんですが... 終了後にコード検索してみると、偽
useSearchParamsが生えていた。 気づきたかった...
結果発表
今年も結果発表が長引いている。デジャヴですか??がんばって...
[20:39] 結果発表がそろそろ行われるとのこと。
気になって Koyeb ダッシュボードを見に行くと、計測されたっぽいような痕跡が。
ワンチャンあるか?

[20:48] 第 3 位が発表される。ワンチャンなかった w
700 点未満はレギュレーションチェックされてないとのことだったが、 Sor4chi 曰く「一応学生のチェックはした、たぶん大丈夫だった」とのこと。
Koyeb ダッシュボードに表示されているアクセス時間的に、自分が最後だったんだろうなぁと。
ということで、

正真正銘レギュ落ちなし (?) の学生 2 位、全体 6 位でした! やった!! (物は言いよう)
上位勢レギュ落ちしすぎや... こわい...
まとめ
レギュ落ち怖いね (他人事) 来年はできるだけ対策して頑張ります