再利用可能なコメントオーバーレイ描画エンジンです。ニコニコ動画のような横流れコメントを、HTML5 動画など任意の再生コンテンツ上に重ねて表示できます。npm レジストリで公開されており、bun add comment-overlay で導入できます。
この内容は findings.md にまとめています。
次のバージョンはバグを含むため非推奨です: v1.2.0, v1.2.1, v1.2.2, v2.5.0
ライブラリは CommentRenderer を中心に構成されています。以下は動画要素にコメントを重ねる最小限の例です。
import { CommentRenderer, cloneDefaultSettings, type RendererSettings } from "comment-overlay";
const video = document.querySelector("video");
const container = document.querySelector(".overlay-container");
if (!(video instanceof HTMLVideoElement) || !(container instanceof HTMLElement)) {
throw new Error("動画要素または描画コンテナが見つかりません。");
}
const settings: RendererSettings = cloneDefaultSettings();
const renderer = new CommentRenderer(settings, {
loggerNamespace: "MyOverlay",
});
renderer.initialize({ video, container });
renderer.addComment("このコメントは明朝体で赤色で下部に大きく表示されます", 5000, [
"shita",
"red",
"big",
"mincho",
]);
// 動画のライフサイクルに合わせてリソースを解放します。
video.addEventListener("ended", () => {
renderer.destroy();
});フルスクリーン表示でもコメントを重ねる場合は、video.requestFullscreen() ではなく、renderer.initialize({ video, container }) に渡したオーバーレイコンテナへ container.requestFullscreen() を呼び出してください。レンダラーはフルスクリーン変更時にキャンバスをコンテナへ追従させ、動画より前面に配置します。
第2引数 vposMs はコメントを表示するミリ秒単位の再生位置です。
校正用途では renderer.addComment(text, vposMs, commands, meta) の第4引数に no、fork、source、threadId、date、userIdHash を渡せます。これらは描画挙動には影響せず、重複判定と校正 trace / frame snapshot のコメント同一性復元に使用されます。
対応コメントコマンドは以下の通りです。
- 位置指定:
shita,ue,naka - サイズ指定:
small,medium,big - フォント指定:
defont(システムフォント),gothic(ゴシック体),mincho(明朝体) - 色指定:
white,red,pink,orange,yellow,green,cyan,blue,purple,black,white2,red2,pink2,orange2,yellow2,green2,cyan2,blue2,purple2,black2 - 透明度指定:
_live(半透明),invisible(非表示) - 幅指定:
full(固定コメントの幅上限を表示幅まで広げる。横流れコメントの幾何・速度には影響しない) - 色指定:
#FF0000,#00FF00,#0000FFなどの16進数カラーコード - 字間指定:
ls:10やletterspacing:10(px単位) - 行高指定:
lh:1.5やlineheight:150%(倍率またはパーセント) - コメントコマンドが未指定のときは
nakamediumdefontwhite相当の表示になります。
small / medium / big のフォント比率、gothic のフォント候補、多行コメントの内部テクスチャ寸法は、ニコニコ動画実プレイヤーの Canvas 描画ログに基づいて調整しています。公式の内部 1364×768 Canvasを表示領域へ比例縮小する寸法則に合わせ、行数起因の自動縮小(big=3、medium=5、small=7、ただしenderを除く)を適用します。
横流れコメントは最大行の実測文字幅を縮小せず、内部1024幅相当の描画領域を文字全体が4秒で横切る速度で移動し、同一レーンは文字幅÷速度後に再利用します。
通常固定コメントは実測文字幅を表示幅の75%、fullは表示幅までへフィットし、その結果の文字サイズ・行高から可変高区間を予約します。固定コメントは3秒間、上固定なら上から、下固定なら下から空き区間へ整列します。単体が表示高未満で空きだけが不足する場合はランダムYへ退避し、単体が表示高以上の場合は同じ端へ重ねます。この境界により、複数レイヤーで構成された矩形・白抜き・歌詞を同じ座標に保ちます。
動画終端3000ms以内のコメントは、固定・横流れとも表示基準時刻を終端3000ms前へ丸め、その後は通常と同じ保持・移動規則を使います。
ca コマンドは専用描画経路を持たず、通常コメントと同じレンダリングパイプラインで処理されます。
ngWords: 入力テキストをトリムしたうえで部分一致 (大文字小文字を区別せず) による NG 判定を行います。ngRegexps: コメント本文に対して評価される正規表現文字列の配列です。空配列を渡すと無効になります。scrollDirection:'rtl'(右→左) または'ltr'(左→右) を指定して、横流れコメントの方向を切り替えられます。scrollVisibleDurationMs: 既定のnullでは公式の4秒横断式を使います。数値を指定すると描画領域と文字全体を横切る時間を上書きします。
コメントデータを処理する際、.trim() を使用しないでください。.trim() は全角スペース(\u3000)や非破壊スペース(\u00A0)を削除するため、AAコメント(アスキーアート)のインデント構造が崩れる原因となります。
// ❌ 悪い例
const text = commentData.body.trim();
renderer.addComment(text, vposMs, commands);
// ✅ 良い例
const text = commentData.body;
if (text.length > 0) {
renderer.addComment(text, vposMs, commands);
}コメントの表示/非表示を動的に切り替えるには setCommentVisibility() メソッドを使用します。
// コメントを非表示にする(即座にキャンバスがクリアされる)
renderer.setCommentVisibility(false);
// コメントを再表示する(描画が再開される)
renderer.setCommentVisibility(true);注意: renderer.settings.isCommentVisible = false のように直接設定を変更すると、キャンバスがクリアされずコメントがフリーズした状態になります。必ず setCommentVisibility() メソッドを使用してください。
ライブラリは、エポック変更や内部状態の変化をイベントフック経由で通知できます。
import {
CommentRenderer,
cloneDefaultSettings,
type CommentRendererEventHooks,
type EpochChangeInfo,
type RendererStateSnapshot,
} from "comment-overlay";
const eventHooks: CommentRendererEventHooks = {
onEpochChange: (info: EpochChangeInfo) => {
console.log(`エポック変更: ${info.previousEpochId} → ${info.newEpochId} (${info.reason})`);
},
onStateSnapshot: (snapshot: RendererStateSnapshot) => {
console.log("状態スナップショット:", snapshot);
},
};
const renderer = new CommentRenderer(cloneDefaultSettings(), {
loggerNamespace: "MyOverlay",
eventHooks,
debug: {
enabled: true, // デバッグログを有効化
},
});エポック変更は次のタイミングで通知されます。
source-change: 動画ソースが変更されたときmetadata-loaded: 動画のメタデータがロードされたとき
デバッグログを有効にすると、内部状態の変化がコンソールに出力されます。
- ライブラリをビルドします:
bun run build - サンプルサーバーを起動します:
bun run serve - ブラウザーで表示される URL を開き、
overlay-tests内のテスト UI でコメント描画を確認できます。
サンプル UI は overlay-tests ディレクトリにあり、scripts/sync-overlay-tests.mjs によってビルド成果物と同期されます。overlay-tests/fixtures/ へ <case-id>-comments.json を追加すると、CASEプルダウンへ自動的に追加されます。コメントJSONはコメント配列、従来のトップレベル comments 配列、またはニコニコの現行コメントAPI応答 { "meta": ..., "data": { "threads": ... } } を使用できます。現行API応答では全mainスレッドを読み込み、source: "trunk" のコメントがあれば従来どおりそれを表示対象にします。トップレベルの "label" は任意で、省略時はcase IDを表示します。同名の <case-id>.mp4 があれば背景動画として使用し、動画がなければコメント末尾から再生時間を生成した無音タイムラインで、再生・停止・シークを含めてコメントだけを確認できます。動画fixtureはgit管理対象外です。
bun run serve はcase一覧の生成とoverlay test用TypeScriptのビルドを行ってからサーバーを起動します。UI ではNGワード、NG正規表現、スクロール方向などをリアルタイムで変更できます。
この内容は findings.md にまとめています。
この内容は findings.md にまとめています。
この内容は findings.md にまとめています。
この内容は findings.md にまとめています。
バグ報告や機能提案は GitHub Issue テンプレートを利用してください。Pull Request を送る際は、テンプレートのチェックリストに従ってローカルでの検証を済ませてから提出してください。
このプロジェクトは MIT License の下で提供されています。
この内容は findings.md にまとめています。
プロジェクト固有の構成・環境・調査知見は findings.md、作業規範は AGENTS.md、検証記録は verification.md を参照してください。
