
Presentation APIを使ってみる
目次
今作っている Fusuma というスライドツールに Presentation API を追加してみました。
GitHub - hiroppy/fusuma: ✍️ Fusuma makes slides with Markdown easily.
✍️ Fusuma makes slides with Markdown easily. Contribute to hiroppy/fusuma development by creating an account on GitHub.
Presentation API とは?
Presentation API - Web APIs | MDN
The Presentation API lets a user agent (such as a Web browser) effectively display web content through large presentation devices such as projectors and network-connected televisions. Supported types of multimedia devices include both displays which are wired using HDMI, DVI, or the like, or wireless, using DLNA, Chromecast, AirPlay, or Miracast.

ステータスは 勧告候補 で、今現在は chrome のみ実装されています。 この API は、Keynote や PowerPoint でプレゼンテーションモードにすると他のディスプレイに出力できるようになる機能をブラウザで実現できます。 ディスプレイの他にも、Chrome Cast や AirPlay 等にも映し出すことも可能です。
とりあえずディスプレイが一枚繋がっているかネットワーク以下に Chrome Cast 等があれば使えます。
いままではどのようにやっていたか?
今までもこのように別画面でフルスクリーンにスライドを表示する実装は出来ていました。 これを実現するためには、localStorage でページ情報と挿入イベントのリッスンを行います。 大きな違いとしては、自動で他のディスプレイにフルスクリーンで表示できる点が大きいです。
今までの場合は、window.open
でレシーバー側を表示し、それを他のディスプレイに移し、フルスクリーンにするという手順が必要でした。
これからは、対応していなければ localStorage の方を使う実装となりました。 また、Presentation API で表示されたレシーバーからは localStorage は参照できません。
Chrome のサンプルコードは以下を参照
Presentation Receiver API Sample
Sample illustrating the use of Presentation Receiver API.
コントローラー
接続処理
presentation api
async function connect() {
return new Promise((resolve, reject) => {
const presentationRequest = new PresentationRequest(["?presenter=view"]);
navigator.presentation.defaultRequest = presentationRequest;
presentationRequest.addEventListener("connectionavailable", (e) => {
const presentationConnection = e.connection;
resolve(presentationConnection); // presentationConnectionからternimate以外の操作を行う
});
presentationRequest.start().catch((err) => reject(err));
});
}
localStorage
localStorage なので特になし
メッセージング
presentation api
presentationConnection.send(
JSON.stringify({
page: 1,
}),
);
localStorage
localStorage.setItem(
"page",
JSON.stringify({
date: Date.now(),
page: 1,
}),
);
レシーバー
接続処理
presentation api
接続という処理はなく、イベントを登録していく感じとなります。
navigator.presentation.receiver.connectionList
に接続されているコネクションのリストが入っています。
function addEvent(name, cb) {
if (navigator.presentation && navigator.presentation.receiver) {
navigator.presentation.receiver.connectionList.then((list) => {
list.connections.forEach((connection) => {
connection.addEventListener(name, cb);
});
list.addEventListener("connectionavailable", (event) => {
cb(event.connection);
});
});
}
}
addEvent("close", (e) => {
console.log(e);
});
localStorage
localStorage なので特になし
メッセージング
presentation api
navigator.presentation.receiver.connectionList.then((list) => {
list.connections.forEach((connection) => {
connection.addEventListener("message", (e) => {
const page = JSON.parse(e.data).page;
});
});
});
localStorage
window.addEventListener("storage", (e) => {
if (e.key === "page") {
const page = JSON.parse(e.newValue).page;
window.slide.bespoke.slide(page);
}
});
問題点
- ライブリロードされると、レシーバーが破棄されるため毎回開き直し
- レシーバー側では、ショートカットキーが使えず、右クリックから検証を押さないとインスペクターが開けない
- シークレットウィンドウだと、起動はするがメッセージが送れない
UnknownError: Mismatch in incognito status: request = 1, response = 0
とりあえずデバッグが大変めんどくさかったです。
今後
Presentation API と localStorage の互換性があるライブラリを書くかもしれません。 しかし、Chrome Cast などはブラウザの域から外れ、localStorage では対応できないため完璧な対応はできないと思います。 message 周りを抽象化したいなーって実装してて感じました。
すごい便利な機能なので早くほかのブラウザにも入ってほしいと思っています。