loadshowは、Webページの読み込みプロセスを動画に記録するオープンソースのCLIツールです。
- Webページの読み込みスピードの比較は感覚と記憶に頼りがち
- PageSpeed Insightsなどの数値による評価は専門家以外にわかりにくい
このような問題意識から、Webページの読み込みスピードをわかりやすく表現し、直感的に比較する方法として開発しました。
読み込みスピードの改善結果の前後や、競合サイトとの比較などに活用ください。
- Node.js >= 20
- Chrome または Chromium
- ffmpeg
npm i -g loadshowrecordサブコマンドでWebページの読み込み過程を動画に記録します。URLと成果物を格納するディレクトリを指定します。
loadshow record https://apple.com/ ./loadshow.mp4上記のコマンドは、https://apple.com/の読み込み過程を記録した./loadshow.mp4を作成します。
juxtaposeサブコマンドで、複数の動画を左右に並べた比較動画を作成できます。
loadshow juxtapose -o compare.mp4 apple.com.mp4 microsoft.com.mp4上記のコマンドは、apple.com.mp4とmicrosoft.com.mp4を左右に配置し、-oオプションに指定したcompare.mp4として出力します。
loadshowはデフォルトで決定論的な記録を行います。これは、Webページの読み込みを可能な限り再現可能にし、記録動画を比較しやすくするための機能です。
決定論的な記録で行われる処理:
- アニメーションとトランジションの無効化 - CSSアニメーション、トランジション、Web Animations APIを停止
- 時刻と乱数の固定化 -
Date.now(),Math.random(),performance.now()を固定値に置き換え - メディアの自動再生停止 - 動画・音声の自動再生を防止、カルーセルなどの自動スクロールも停止
- 遅延読み込みの即時化 - IntersectionObserverをモックし、画面外の画像も即座に読み込み
- スクロール動作の無効化 -
scrollTo(),scrollIntoView()などを無効化 - フォントと画像の読み込み待機 - すべてのフォントと画像の読み込み完了を待機(タイムアウト5秒)
- 外部リソースのブロック - 指定されたURLパターンの読み込みをブロック可能
- 要素の出現待機 - SPAなどで動的に表示される要素のセレクタを指定して待機可能
この機能により、動的な要素やアニメーションの影響を受けず、純粋な読み込み速度の比較が可能になります。
従来の非決定論的な記録(アニメーションなどが動作する状態)を行いたい場合は、-dまたは--dynamicオプションを指定します。
# 決定論的な記録(デフォルト)
loadshow record https://apple.com/ ./static.mp4
# 動的な記録(アニメーションあり)
loadshow record -d https://apple.com/ ./dynamic.mp4動的な記録では、Webページの実際の挙動(アニメーション、自動再生、遅延読み込みなど)がそのまま記録されます。
次の項目を環境変数で指定できます。
LOG_LEVELログレベル (fatal|warn|error|info|debug|fatal) デフォルト値infoCHROME_PATHChromeのパス (例/Applications/Google Chrome.app/Contents/MacOS/Google Chrome)FFMPEG_PATHffmpegのパス (例/opt/homebrew/bin/ffmpeg) デフォルト値ffmpeg(Windowsの場合ffmpeg.exe)LC_ALLorLC_MESSAGESorLANG情報バナーのロケール (例ja-JP)TZ情報バナーのタイムゾーン (例Asia/Tokyo)BARE_PUPPETEER実験的なモード。puppeteerにバンドルされるChromeを使用 (例:1)LOG_OBJECTSログに付帯されたオブジェクトを表示する。
動画の仕様は次のように構造化されたオブジェクトで規定されています。それぞれのデフォルト値とともに示します。
frameFormat: 'png' # 中間画像フォーマット (pngまたはjpeg。jpegの方がやや軽量)
frameQuality: 85 # 中間画像フォーマットがjpegの場合の品質値
hasBanner: true # 情報バナーの表示
hasProgressBar: true # プログレスバーの表示
layout:
canvasWidth: 512 # 動画エリアの幅
canvasHeight: 640 # 動画エリアの高さ
columns: 3 # カラム数
gap: 20 # カラム間の幅
padding: 20 # 動画エリアの余白
borderWidth: 1 # 罫線の幅
indent: 20 # 第二カラム移行の頭下げ
outdent: 20 # 第一カラムの下方の余白
progressHeight: 16 # プログレスバーの幅
recording:
network: # ネットワーク設定
latencyMs: 20 # レイテンシー (単位 ms)
downloadThroughputMbps: 10 # ダウンロードスループット (単位 Mbps)
uploadThroughputMbps: 10 # アップロードスループット (単位 Mbps)
cpuThrottling: 4 # CPUスロットリング (スマホ相当にするため4 = 性能1/4)
headers: # HTTPリクエストヘッダ
viewportWidth: 375 # ビューポート幅
timeoutMs: 30000 # タイムアウト (単位 ms)
preferSystemChrome: false # Puppeteerバンドルのブラウザではなくインストール済みChromeを優先 ※
puppeteer: # puppeteerの設定 `PuppeteerLaunchOptions`
headless: true,
args: # 文字列
- '--hide-scrollbars'
deterministic: # 決定論的な記録の設定
enabled: true # 決定論的な記録を有効化 (デフォルト: true、-dオプションでfalse)
mockTime: '2025-01-01T00:00:00Z' # 固定する時刻
randomSeed: 42 # 乱数シード
disableAnimations: true # アニメーションを無効化
disableAutoplay: true # 自動再生を無効化
disableScroll: true # スクロールを無効化
fixIntersectionObserver: true # IntersectionObserverを即時実行
disableWebAnimations: true # Web Animations APIを無効化
maskSelectors: [] # 非表示にするセレクタのリスト
blockUrls: [] # ブロックするURLパターンのリスト
waitForFonts: true # フォント読み込み完了を待機
waitForImages: true # 画像読み込み完了を待機
waitSelectors: [] # 出現を待機するセレクタのリスト
banner: # 情報バナー
templateFilePath: "" # HTMLテンプレート (ファイル指定)
htmlTemplate: "" # HTMLテンプレート (テキスト指定)
vars: # HTMLテンプレートに渡す変数 (オブジェクト)
# var1: "値1"
composition:
colorTheme:
background: '#eee' # 動画エリアの背景
border: '#ccc' # 罫線
progressBackground: '#fff' # プログレスバーの背景
progressForeground: '#0a0' # プログレスバー
progressText: '#fff' # プログレスバーの文字
progressTimeText: '#333' # 経過時間の文字
rendering:
outroMs: 1000 # 読み込み完了後の静止時間 (単位 ms)
ffmpegArgs: # ffmpegへの追加オプション (文字列配列)
# - "..."
# recording.preferSystemChromeは環境変数BARE_PUPPETEER=1の場合のみ使用これらの値を必要に応じて一部上書きして、作成される動画をカスタマイズできます。値を上書きする方法はふたつあります。
# -u オプションによる上書き (複数可)
loadshow record -u "layout.canvasWidth=500" -u "frameFormat: jpeg" https://apple.com/ ./loadshow.mp4
# YAMLファイルでまとめて変更する場合
loadshow record -s spec.yml https://apple.com/ ./loadshow.mp4spec.ymlには、以下の例のように上書きしたい属性のみ記述します。
frameFormat: jpeg
layout:
canvasWidth: 500インストール済みのChromeを自動で検索します。
環境変数CHROME_PATHの指定がある場合、優先で適用します。
以下はOSごとのシステムChromeの代表的なパスです。
- MacOS
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome - Windows
C:\Program Files\Google\Chrome\Application\chrome.exe - Linux
/usr/bin/google-chrome
puppeteerをインストールするとChromiumブラウザが~/.cacheディレクトリ以下にインストールされます。
本来はこちらを使いたいところですが、環境によって動画の表示崩れが発生するため、Chromeの事前インストールをを必要としています。
環境変数BARE_PUPPETEERに値(1など)を設定すると、実験的にpuppeteerがバンドルしたブラウザを使用します。
動画の上部に表示される情報バナーは、HTMLテンプレート(handlebars)の変数を書き換えることによりカスタマイズできます。
変数自体がテンプレートとして振る舞うので、{{ }}などの記号を記述することで他の変数を参照できます。
# システムから渡される変数
width: 動画の幅
url: 記録しているURL
htmlTitle: HTMLタイトル
timestampMs: 記録を行った日時のタイムスタンプ(単位 ms)
resourceSizeBytes: リソースサイズ
onLoadTimeMs: 読み込み時間(OnLoad 単位 ms)
# 組み込みヘルパー
datetime: ロケールに合わせた日時タイムスタンプの整形
mb: リソースサイズをMB単位に整形
msToSec: msを秒に整形
i18n: 一部の用語を翻訳仕様オプションbanner.vars以下の値を変更することでカスタマイズできます。
banner:
vars:
mainTitle: "{{htmlTitle}}"
subTitle: "{{url}}"
credit: "loadshow"
createdAt: "{{datetime timestampMs}}"
resourceSizeLabel: "Resource Size"
resourceSizeValue: "{{mb resourceSizeBytes}}"
onLoadTimeLabel: "OnLoad Time"
onLoadTimeValue: "{{msToSec onLoadTimeMs}}"例えばloadshowというクレジット表記を変更するには、次のようにコマンドを実行します。
loadshow record -u "banner.vars.credit=My Loadshow!" https://apple.com/ ./loadshow.mp4仕様オプションbanner.templateFilePathにHTMLテンプレートのパスを指定することで、テンプレート自体を変更できます。
あるいはbanner.htmlTemplateにHTMLテンプレートを直接指定することもできます。この利用方法はYAMLファイルによる仕様の指定を想定しています。
詳しくはsrc/banner.tsを参照してください。
内部的には縦長のウィンドウを作成し、動画の記録を行っています。そのため全面背景のようにウィンドウの高さを基準にしたCSSを利用しているページでは、表示が崩れる場合があります。
プログラムの一部として呼び出すには以下のように記述します。
import Fs from 'fs'
import { Dependency, runLoadshow, defaultLoadshowSpec, mergeLoadshowSpec } from 'loadshow'
async function main() {
// 外部依存
const dependency = new Dependency()
// Pino loggerをカスタマイズ / 削除可能
// dependency.logger = ... / delete dependency.logger
// 動画の仕様カスタマイズ
const spec = mergeLoadshowSpec(defaultLoadshowSpec(), {
layout: {
canvasWidth: 500,
},
frameFormat: 'jpeg'
})
// 仕様・URL・動画パス・成果物ディレクトリを入力とする
Fs.mkdirSync('./loadshow', { recursive: true })
const input = {
...spec,
url: 'https://github.com/',
videoFilePath: './loadshow.mp4'
artifactsDirPath: './loadshow',
}
// loadshowの実行
const output = runLoadshow(spec, dependency)
// 結果の参照
console.log(output)
}
main()Apache License 2.0 の下で公開します。
技術サポートを希望の方は問い合わせください。
git clone <url>
cd <dir>
yarnMacOS Arm64の場合、モジュールsharpの関係で次のコマンドが必要な模様。
yarn --ignore-enginesyarn testテストカバレッジは進捗予定。
src/adhoc.tsを改変して以下のプログラムを実行。
yarn adhoc- テストとかかなり適当。
puppeteerにバンドルされるブラウザの積極利用。- Chromeの事前インストールを不要にしたい。
- 環境変数
BARE_PUPPETEERに値を設定することで試験可能。