TL;DR:
- Supabase、React、WebGazer.js、Motion One、anime.js、Stable Audio で構築
- Supabase のリアルタイム プレゼンスとブロードキャストを活用します (データベース テーブルはまったく使用されません!)
- GitHub リポジトリ
- ウェブサイト
- デモビデオ
さらに別の Supabase Launch Week Hackathon と、Gaze into the Abyss と呼ばれるもう 1 つの実験的プロジェクトです。 これは、最終的には最も単純なプロジェクトであると同時に、最も複雑なプロジェクトの 1 つとなりました。幸いなことに、私は最近 Cursor をかなり楽しんでいたので、なんとかやり遂げるのにいくつかの助けがありました。また、私の心の中の疑問を検証したいと思いました。データベース テーブルを使わず、Supabase のリアルタイム機能だけを使用することは可能ですか? (多少明白かもしれない) 答えは次のとおりです: はい、そのとおりです (リアルタイム チーム、愛しています ♥️)。それでは、実装についてもう少し詳しく見てみましょう。
アイデア
私はある日、深淵についてのニーチェの名言について考えていました。それを何らかの形で実際に視覚化できたら素敵 (そしてクール) だろう、と考えていました。暗い画面を見つめると、何かがあなたを見つめ返します。それ以上のことは何もありません!
プロジェクトの構築
最初は Three.js を使用してこのプロジェクトを作成しようと考えていましたが、3D の目用に無料のアセットを作成または検索する必要があることに気付きました。特にプロジェクト自体に取り組む時間があまりなかったので、それは少しやりすぎだと判断し、代わりに SVG を使用して 2D で行うことにしました。
また、視覚だけのものにはしたくありませんでした。音声もあればより良い体験になるでしょう。そこで私は、参加者がマイクに向かって話せ、他の人にはそれが不適格なささやき声や通り過ぎる風として聞こえたら素晴らしいだろうというアイデアを思いつきました。しかし、これは非常に困難であることが判明し、WebAudio と WebRTC をうまく接続できなかったため、完全に削除することにしました。コードベースには、ローカルのマイクをリッスンして現在のユーザーに「風の音」をトリガーするコンポーネントが残っていますので、ご覧になりたい場合はご覧ください。将来何か追加されるかもしれません?
リアルタイムルーム
ビジュアル関連の作業をする前に、考えていたリアルタイム設定をテストしたいと思いました。リアルタイム機能にはいくつかの制限があるため、次のように機能するようにしました。
- 最大数があります。 1 つのチャンネルに一度に 10 人の参加者が参加可能
- つまり、チャンネルがいっぱいの場合は、新しいチャンネルに参加する必要があります
- 他の参加者の目だけを見るべきです
このために、次のようなリアルタイム チャネルに再帰的に参加する useEffect セットアップを思いつきました。
この joinRoom は useEffect フック内に存在し、ルーム コンポーネントがマウントされるときに呼び出されます。この機能に取り組んでいるときに気づいた注意点の 1 つは、currentPresences パラメータが利用可能であっても、join イベントに値が含まれていないということでした。それが実装のバグなのか、それとも意図したとおりに動作しているのかはわかりません。したがって、ユーザーが参加するたびにルームの参加者の数を取得するには、手動で room.presenceState フェッチを実行する必要があります。
参加者数を確認し、現在のルームから登録を解除して別のルームへの参加を試みるか、現在のルームから続行します。同期が遅すぎるため、これを参加イベントで行います (イベントの参加または退出後にトリガーされます)。
ブラウザでたくさんのタブを開いてこの実装をテストしましたが、どれも充実しているようでした。
その後、マウス位置の更新を使用してソリューションをデバッグしたいと思いましたが、チャネル内で送信するメッセージが多すぎるという問題がすぐに発生しました。解決策: 通話を抑制します。
/** * Creates a throttled version of a function that can only be called at most once * in the specified time period. */ function createThrottledFunction<t extends unknown> unknown>( functionToThrottle: T, waitTimeMs: number ): (...args: Parameters<t>) => void { let isWaitingToExecute = false return function throttledFunction(...args: Parameters<t>) { if (!isWaitingToExecute) { functionToThrottle.apply(this, args) isWaitingToExecute = true setTimeout(() => { isWaitingToExecute = false }, waitTimeMs) } } } </t></t></t>
Cursor はこの小さなスロットル関数作成ツールを思いつき、これを次のような視線追跡ブロードキャストで使用しました。
const throttledBroadcast = createThrottledFunction((data: EyeTrackingData) => { if (currentChannel) { currentChannel.send({ type: 'broadcast', event: 'eye_tracking', payload: data }) } }, THROTTLE_MS) throttledBroadcast({ userId: userId.current, isBlinking: isCurrentlyBlinking, gazeX, gazeY })
とても役に立ちました!また、初期のバージョンではアイトラッキング メッセージをプレゼンス付きで送信していましたが、ブロードキャストでは 1 秒あたりにより多くのメッセージが許可されるため、代わりに実装をそれに切り替えました。カメラは常にすべてを記録するため、視線追跡では特に重要です。
アイトラッキング
このプロジェクトのアイデアを最初に思いついたとき、しばらく前に WebGazer.js に遭遇しました。これは非常に興味深いプロジェクトで、驚くほどうまくいきます!
視線追跡機能全体は、useEffect フック内の 1 つの関数で実行されます。
window.webgazer .setGazeListener(async (data: any) => { if (data == null || !currentChannel || !ctxRef.current) return try { // Get normalized gaze coordinates const gazeX = data.x / windowSize.width const gazeY = data.y / windowSize.height // Get video element const videoElement = document.getElementById('webgazerVideoFeed') as HTMLVideoElement if (!videoElement) { console.error('WebGazer video element not found') return } // Set canvas size to match video imageCanvasRef.current.width = videoElement.videoWidth imageCanvasRef.current.height = videoElement.videoHeight // Draw current frame to canvas ctxRef.current?.drawImage(videoElement, 0, 0) // Get eye patches const tracker = window.webgazer.getTracker() const patches = await tracker.getEyePatches( videoElement, imageCanvasRef.current, videoElement.videoWidth, videoElement.videoHeight ) if (!patches?.right?.patch?.data || !patches?.left?.patch?.data) { console.error('No eye patches detected') return } // Calculate brightness for each eye const calculateBrightness = (imageData: ImageData) => { let total = 0 for (let i = 0; i = SAMPLES_SIZE) { brightnessSamples.current.shift() // Remove oldest sample } brightnessSamples.current.push(avgBrightness) // Calculate dynamic threshold from rolling average const rollingAverage = brightnessSamples.current.reduce((a, b) => a + b, 0) / brightnessSamples.current.length const dynamicThreshold = rollingAverage * THRESHOLD_MULTIPLIER // Detect blink using dynamic threshold const blinkDetected = avgBrightness > dynamicThreshold // Debounce blink detection to avoid rapid changes if (blinkDetected !== isCurrentlyBlinking) { const now = Date.now() if (now - lastBlinkTime > 100) { // Minimum time between blink state changes isCurrentlyBlinking = blinkDetected lastBlinkTime = now } } // Use throttled broadcast instead of direct send throttledBroadcast({ userId: userId.current, isBlinking: isCurrentlyBlinking, gazeX, gazeY }) } catch (error) { console.error('Error processing gaze data:', error) } })
ユーザーが見ている場所の情報を取得するのは簡単で、画面上のマウスの位置を取得するのと同じように機能します。ただし、まばたき検出を (クールな) 機能として追加したいとも思いましたが、これにはいくつかの困難を乗り越える必要がありました。
WebGazer とまばたき検出に関する情報を Google で検索すると、初期実装の名残がいくつか確認できます。ソース内にコメントアウトされたコードがあるように。残念ながら、この種の機能はライブラリには存在しません。手動で行う必要があります。
多くの試行錯誤の後、Cursor と私は、眼帯データからピクセルと輝度レベルを計算して、ユーザーがいつまばたきしているかを判断するソリューションを思いつくことができました。また、(少なくとも私にとっては)照明によってはウェブカメラがユーザーのまばたきを常に認識するとは限らないことに気づいたため、動的な照明調整もいくつかあります。私の場合、写真や部屋が明るいほどうまく機能しませんが、暗い照明ではうまくいきます (図をご覧ください)。
視線追跡機能 (WebGazer には、画面上に赤い点を表示して、どこを見ているかを視覚化する非常に優れた .setPredictionPoints 呼び出しがあります) をデバッグしているときに、調整しない限り、追跡があまり正確ではないことに気付きました。 これは、プロジェクトがルームに参加する前に行うように求めていることです。
/** * Creates a throttled version of a function that can only be called at most once * in the specified time period. */ function createThrottledFunction<t extends unknown> unknown>( functionToThrottle: T, waitTimeMs: number ): (...args: Parameters<t>) => void { let isWaitingToExecute = false return function throttledFunction(...args: Parameters<t>) { if (!isWaitingToExecute) { functionToThrottle.apply(this, args) isWaitingToExecute = true setTimeout(() => { isWaitingToExecute = false }, waitTimeMs) } } } </t></t></t>
これが実際に動作しているのを見るのは、とても素晴らしい経験でした。同じアプローチを周囲の線にも適用し、それらを中心に向かって「折りたたむ」ように Cursor に指示しました。これはほぼ 1 回の操作で完了しました!
その後、目は、部屋全体が大きな目のように見えるようにセルが配置された単純な CSS グリッド内にレンダリングされます。
const throttledBroadcast = createThrottledFunction((data: EyeTrackingData) => { if (currentChannel) { currentChannel.send({ type: 'broadcast', event: 'eye_tracking', payload: data }) } }, THROTTLE_MS) throttledBroadcast({ userId: userId.current, isBlinking: isCurrentlyBlinking, gazeX, gazeY })
最後の仕上げ
その後、素敵なイントロ画面と BGM を追加すれば、プロジェクトは準備完了です!
このような作業をしているとき、オーディオは常にエクスペリエンスを向上させます。そのため、ユーザーが「深淵に入る」ときに、安定したオーディオを使用して BGM を生成しました。私が音楽に使用したプロンプトは次のとおりです:
アンビエント、不気味、BGM、ささやき声、風、スローテンポ、不気味、深淵
また、ただの黒い画面では少し退屈だと思ったので、背景にアニメーションの SVG フィルターを追加しました。さらに、画面の中央に暗くぼやけた円を追加して、素敵なフェード効果を加えました。おそらく SVG フィルターを使用してこれを行うこともできましたが、これにあまり時間をかけたくありませんでした。次に、さらに動きを持たせるために、背景を軸を中心に回転させました。 SVG フィルターを使用してアニメーションを実行するのは少し不安定な場合があるため、代わりにこの方法で行うことにしました。
<div> <h2> 結論 </h2> <p>これで終わりです。Supabase のリアルタイム機能を使用して様式化された視線追跡を実装する方法を非常に簡単に説明しました。個人的には、これは非常に興味深い実験であり、作業中にあまり問題はありませんでした。そして驚くべきことに、プロジェクトを提出する前の最後の夜に徹夜する必要はありませんでした!</p> <p>プロジェクトやその結果がどうなったかをデモビデオで気軽にチェックしてください。大勢の人が同時にそれを使用している場合、いくつかの問題が発生する可能性があります(適切に実行するには複数のデバイスとウェブカメラが必要なため、テストが非常に困難です)が、それはハッカソンプロジェクトのやり方だと思いますか?そして、それをテストする場合は、目が見えたら、それはインターネット上のどこかで別の誰かがあなたを見ているということを忘れないでください!</p> </div>
以上がSupabase と WebGazer.js を使用してリアルタイムの視線追跡エクスペリエンスを構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

node.jsは、主にストリームのおかげで、効率的なI/Oで優れています。 ストリームはデータを段階的に処理し、メモリの過負荷を回避します。大きなファイル、ネットワークタスク、リアルタイムアプリケーションの場合。ストリームとTypeScriptのタイプの安全性を組み合わせることで、パワーが作成されます

PythonとJavaScriptのパフォーマンスと効率の違いは、主に以下に反映されています。1)解釈された言語として、Pythonはゆっくりと実行されますが、開発効率が高く、迅速なプロトタイプ開発に適しています。 2)JavaScriptはブラウザ内の単一のスレッドに限定されていますが、マルチスレッドおよび非同期I/Oを使用してnode.jsのパフォーマンスを改善でき、両方とも実際のプロジェクトで利点があります。

JavaScriptは1995年に発信され、Brandon Ikeによって作成され、言語をCに実現しました。 2。JavaScriptのメモリ管理とパフォーマンスの最適化は、C言語に依存しています。 3. C言語のクロスプラットフォーム機能は、さまざまなオペレーティングシステムでJavaScriptを効率的に実行するのに役立ちます。

JavaScriptはブラウザとnode.js環境で実行され、JavaScriptエンジンに依存してコードを解析および実行します。 1)解析段階で抽象的構文ツリー(AST)を生成します。 2)ASTをコンパイル段階のバイトコードまたはマシンコードに変換します。 3)実行段階でコンパイルされたコードを実行します。

PythonとJavaScriptの将来の傾向には、1。Pythonが科学コンピューティングの分野での位置を統合し、AI、2。JavaScriptはWebテクノロジーの開発を促進します。どちらもそれぞれのフィールドでアプリケーションシナリオを拡大し続け、パフォーマンスをより多くのブレークスルーを行います。

開発環境におけるPythonとJavaScriptの両方の選択が重要です。 1)Pythonの開発環境には、Pycharm、Jupyternotebook、Anacondaが含まれます。これらは、データサイエンスと迅速なプロトタイピングに適しています。 2)JavaScriptの開発環境には、フロントエンドおよびバックエンド開発に適したnode.js、vscode、およびwebpackが含まれます。プロジェクトのニーズに応じて適切なツールを選択すると、開発効率とプロジェクトの成功率が向上する可能性があります。

はい、JavaScriptのエンジンコアはCで記述されています。1)C言語は、JavaScriptエンジンの開発に適した効率的なパフォーマンスと基礎となる制御を提供します。 2)V8エンジンを例にとると、そのコアはCで記述され、Cの効率とオブジェクト指向の特性を組み合わせて書かれています。3)JavaScriptエンジンの作業原理には、解析、コンパイル、実行が含まれ、C言語はこれらのプロセスで重要な役割を果たします。

JavaScriptは、Webページのインタラクティブ性とダイナミズムを向上させるため、現代のWebサイトの中心にあります。 1)ページを更新せずにコンテンツを変更できます。2)Domapiを介してWebページを操作する、3)アニメーションやドラッグアンドドロップなどの複雑なインタラクティブ効果、4)ユーザーエクスペリエンスを改善するためのパフォーマンスとベストプラクティスを最適化します。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ホットトピック









