手元の物や書類などをカメラで撮影して画面上に投影する時に使う「書画カメラ」を、WebアプリとしてClaudeCodeで自作しました。単一HTMLファイルでできたシンプルな構成で、PWA(プログレッシブ ウェブアプリ)として扱えるようにしたので、デスクトップやスマホに単体アプリとしてもインストールできます。
DocCam という名前で、GitHubで公開しています。
なぜ作ったか
ワークショップやプレゼンなどで手元の本や書類をカメラで映して説明したい場面があります。市販の書画カメラは専用ハードウェアで数万円するものもあり、特化している分Webカメラほど汎用的ではないので、手軽に導入できるものではありません。一方、PC内蔵カメラ・Webカメラやスマホについているカメラをそのまま使えば、ソフトウェアで十分な機能を実現できるはずです。
既存のWebアプリも探しましたが、ズームや静止・スクリーンショットといった基本機能のほかに、レーザーポインターや台形補正、ルーラー、デュアルカメラ対応など、個人的にあったらいいなと思う機能がなかったり使い勝手がイメージと違ったりしました。それならば自分で作ろうと思い、ClaudeCodeを使って実装しました。
主な機能
カメラ・画面共有
- 接続されたカメラをドロップダウンで選択
- 画面共有(
getDisplayMedia)に対応 - カメラと画面共有を同時に扱えるデュアル表示
デュアルビューのレイアウト
2つのソースを同時に表示する方法は4種類から選べます。
- 左右分割 / 上下分割 — 境界線をドラッグして比率を調整
- コーナーPiP — 小窓を画面の好きな位置に配置、ドラッグ移動・リサイズも可能
- 半透明重畳 — 一方を透かして重ねる
ズーム・パン
スライダーでズーム率を調整し、ドラッグでパン操作ができます。ズームリセット(0キー)や、縦/横フィットのプリセットも備えています。
書画カメラとして使いやすい機能
| 機能 | 説明 |
|---|---|
| レーザーポインター | 8色・4サイズ。クリック前もカーソル位置を点でプレビュー表示 |
| 蛍光ペン | キャンバス上に半透明ストロークを描画。再タップで全消去 |
| スポットライト | 暗転した画面の一部だけを円・楕円・矩形で照らす |
| ルーラー | ドラッグで移動できる定規。十字定規・角度設定にも対応 |
| グリッド | 2/4/9/16分割、またはカスタムマス目サイズ |
| 静止・スクリーンショット | 映像を一時停止してPNG保存 |
| 台形補正 | 上下・左右・角度の台形歪みを補正(プロジェクタースクリーン向き) |
| 画質調整 | 明るさ・コントラスト・彩度・ホワイトバランス・書画カメラ向け自動調整 |
| フラッシュライト | スマホ背面カメラのトーチ制御(MediaTrackCapabilities.torch) |
その他
- 90°回転・左右反転
- ハイコントラストモード
- 9言語対応(EN / 日本語 / 中文 / 한국어 / ES / FR / DE / PT / AR)、ブラウザから自動判定
- 設定を
localStorageに保存・自動復元 - キーボードショートカット
PWAとしてのインストール方法
アプリストア不要で、どのプラットフォームにもインストールできます。
iPhone / iPad(iOS 16.4以降)
- Safari で https://akichika.github.io/doc-cam/ を開く
- 画面下部の共有ボタン(四角+矢印)をタップ
- 「ホーム画面に追加」 をタップ
- 「追加」 をタップ
SafariのみPWAインストール対応。iOS版Chromeなどでは不可。
Android
- Chrome で https://akichika.github.io/doc-cam/ を開く
- 右上の ⋮ メニュー → 「ホーム画面に追加」(または自動表示のインストールバナー)
- 「インストール」 をタップ
Windows / Mac / Linux
- Chrome または Edge で https://akichika.github.io/doc-cam/ を開く
- アドレスバー右端のインストールアイコン(⊕) をクリック
- 「インストール」 をクリック
モバイルの制限事項
| 機能 | iOS Safari | Android Chrome |
|---|---|---|
| カメラ使用 | ✅ | ✅ |
| フロント・背面カメラの同時使用 | ❌ | ❌ |
| 画面共有 | ❌ | ✅ |
| フラッシュライト | ✅(対応機のみ) | ✅(対応機のみ) |
| PWAインストール | ✅ Safari のみ | ✅ Chrome |
iOSではハードウェア制限により、複数カメラの同時使用と画面共有はできません。アプリ側でこれを検出して該当UIを自動的に非表示にしています。
技術構成
index.html(単一ファイル)
├── HTML — UI構造
├── CSS — スタイル・アニメーション
├── JavaScript — すべてのロジック
manifest.json
sw.js(Service Worker)
icon.svg / icon-192.png / icon-512.png
フレームワーク・ビルドツールは一切使用していません。バニラHTML/CSS/JSのみです。
カメラ・映像処理
// カメラストリーム取得
const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: deviceId } }
});
video.srcObject = stream;
// 画面共有取得
const screenStream = await navigator.mediaDevices.getDisplayMedia({
video: { cursor: 'always' },
audio: false
});
映像変換(ズーム・パン・回転・台形補正)はすべてCSSトランスフォームで行っています。
/* ズーム・パン・回転・台形補正の合成 */
transform:
scale(zoomVal)
translate(panX, panY)
rotate(rotateDeg)
perspective(800px)
rotateY(keystoneH)
rotateX(keystoneV)
rotate(keystoneAngle)
scaleX(mirrorX);
ホワイトバランスはSVG feColorMatrix フィルタで実装しています。iOS Safariではvideoタグへの直接適用にバグがあったため、ラッパーdivに適用しています。
PWA対応
Service WorkerとWebマニフェストを組み合わせて、インストール可能なPWAとして動作します。
// sw.js — キャッシュファースト戦略
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => cached || fetch(event.request))
);
});
デュアルカメラの検出(モバイル)
iOSではgetUserMediaを2回呼ぶと最初のストリームが停止します。これをプローブで検出し、非対応の場合はサブカメラUIを非表示にします。
async function probeDualCam() {
if (dualCamProbed || !isMobile() || cameras.length < 2) return;
dualCamProbed = true;
const otherId = cameras.find(c => c.deviceId !== activeCam1Id)?.deviceId;
if (!otherId) return;
try {
const test = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: otherId } }
});
test.getTracks().forEach(t => t.stop());
// プローブ成功後にメインが死んでいればiOS確定
if (mainStreamKilled()) {
disableCam2UI();
await startCamera(activeCam1Id); // メインを再起動
}
} catch(e) {
disableCam2UI();
if (mainStreamKilled()) await startCamera(activeCam1Id);
}
}
キーボードショートカット
| キー | 機能 | キー | 機能 |
|---|---|---|---|
Space / K
|
静止 | L |
レーザーポインター |
C |
スクリーンショット | H |
スポットライト |
M |
左右反転 | U |
ルーラー |
R |
回転 | G |
グリッド |
T |
台形補正 | Y |
蛍光ペン |
Z |
ズームパネル | Q |
画質調整 |
0 |
ズームリセット | V |
レイアウト |
+ / -
|
ズームイン/アウト | X |
ソース入替 |
F |
全画面 | S |
画面共有 |
ClaudeCodeでの開発について
機能の追加・修正はほぼClaudeCodeとの対話で行いました。「ミラー反転しているときにパンの方向が逆になる」「iOSでデュアルカメラ検出するとメインカメラが止まる」といった実機テストで見つかった細かい問題も、再現条件を伝えるとコードを的確に修正してくれました。
単一HTMLファイルという制約の中で、CSS・JS・SVGアイコンをすべてインラインに収める構成でも、コンテキストを保ちながら編集できたのは開発効率の面で大きかったです。
ぜひ授業やワークショップほか様々な用途でご利用ください。
リポジトリ: https://github.com/akichika/doc-cam
フィードバックや不具合報告はIssueでお気軽にどうぞ。



