搜尋
首頁web前端js教程使用 Supabase 和 WebGazer.js 建立即時眼動追蹤體驗

TL;博士:

  • 使用 Supabase、React、WebGazer.js、Motion One、anime.js、穩定音訊建置
  • 利用 Supabase 即時呈現和廣播(完全不使用資料庫表格!)
  • GitHub 儲存庫
  • 網站
  • 示範影片

又一場 Supabase 啟動週黑客馬拉松和另一個實驗項目,名為 凝視深淵。 這最終成為了最簡單又最複雜的項目之一。幸運的是,我最近很喜歡 Cursor,所以我得到了一些幫助來完成它!我還想驗證我心中的一個問題:是否可以使用 Supabase 的即時功能而無需任何資料庫表? (也許有些明顯)答案是:是的,是的(愛你,即時團隊♥️)。因此,讓我們更深入地了解實現。

這個想法

有一天,我隨機想到了尼采關於深淵的名言,如果能夠以某種方式實際想像它會很好(而且很酷):你凝視著黑暗的屏幕,有東西在凝視著你。沒有更多了!

建構專案

最初我的想法是使用 Three.js 來製作這個項目,但我意識到這意味著我需要為 3D 眼睛創建或找到一些免費資源。我認為這有點太多了,特別是因為我沒有太多時間來處理專案本身,因此決定使用 SVG 進行 2D 製作。

我也不希望它只是視覺效果:如果有些音訊也會有更好的體驗。所以我有一個想法,如果參與者可以對著麥克風說話,而其他人可以聽到不合格的低語或風過時的聲音,那就太棒了。然而,這非常具有挑戰性,因此我決定完全放棄它,因為我無法將 WebAudio 和 WebRTC 很好地連接在一起。我的程式碼庫中確實有一個剩餘組件,如果您想看一下,它會監聽本地麥克風並為當前用戶觸發“風聲”。也許將來會添加一些東西?

即時房間

在處理任何視覺內容之前,我想測試一下我想要的即時設定。由於即時功能存在一些限制,我希望它能夠工作,以便:

  • 最多有。一個頻道一次有 10 位參與者
    • 表示如果一個新頻道已滿,您需要加入一個新頻道
  • 你應該只看到其他參與者的眼睛

為此,我想出了一個 useEffect 設置,它遞歸地加入到實時通道,如下所示:





這個 joinRoom 位於 useEffect 鉤子內,並在安裝房間元件時被呼叫。我在開發此功能時發現的一個警告是,currentPresences 參數在連接事件中不包含任何值,即使它可用。我不確定這是否是實施中的錯誤或按預期工作。因此,每當用戶加入時,需要手動獲取 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>

遊標想出了這個小油門函數創建器,我將它與眼動追蹤廣播一起使用,如下所示:

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
})

這很有幫助!另外,在最初的版本中,我在存在狀態下發送了眼動追蹤訊息,但是廣播每秒允許更多訊息,所以我將實現切換到了這一點。這在眼動追蹤中尤其重要,因為相機會一直記錄一切。

眼球追蹤

當我第一次有了這個專案的想法時,我就遇到了 WebGazer.js。這是一個非常有趣的項目,而且效果出奇的好!

整個眼球追蹤功能是在 useEffect 掛鉤中的一個函數中完成的:

    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)
        }
      })

取得使用者正在查看的資訊很簡單,就像取得螢幕上的滑鼠位置一樣。然而,我還想添加眨眼檢測作為(一個很酷的)功能,這需要跳過一些環節。

當您在 google 上搜尋有關 WebGazer 和眨眼偵測的資訊時,您可以看到初始實作的一些剩餘內容。就像原始碼中甚至有註解掉的程式碼一樣。不幸的是,庫中不存在此類功能。您需要手動完成。

經過大量的試驗和錯誤,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>

看到它的實際應用是一次非常酷的體驗!我對周圍的線條應用了相同的方法,並指示遊標將它們向中心“折疊”:它幾乎一氣呵成!

然後,眼睛將在一個簡單的 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
})

最後的潤飾

然後播放一些不錯的介紹螢幕和背景音樂,專案就可以開始了!

當您處理此類事情時,音訊總是可以改善體驗,因此我使用穩定音訊在使用者「進入深淵」時生成背景音樂。我用於音樂的提示如下:

環境、令人毛骨悚然、背景音樂、低語聲、風、慢節奏、怪異、深淵

我還覺得純黑的螢幕有點無聊,所以我在背景上添加了一些動畫 SVG 濾鏡。此外,我在螢幕中央添加了一個黑暗的、模糊的圓圈,以產生一些漂亮的淡入淡出效果。我可能可以使用 SVG 濾鏡來完成此操作,但我不想在這方面花費太多時間。然後為了有更多的運動,我讓背景繞著軸旋轉。有時使用 SVG 濾鏡製作動畫有點奇怪,所以我決定採取這種方式。

 <div>



<h2>
  
  
  結論
</h2>

<p>現在您已經了解了:相當直接地了解如何使用 Supabase 的即時功能實現程式化的眼球追蹤。就我個人而言,我發現這是一個非常有趣的實驗,並且在進行過程中沒有遇到太多問題。令人驚訝的是,在提交專案之前我不需要在最後一晚熬夜! </p>

<p>請隨意查看該專案或示範影片的結果。如果一群人同時使用它,可能會出現一些問題(很難測試,因為它需要多個設備和網路攝影機才能正確完成),但我想這是黑客馬拉松專案的時尚?如果您確實進行了測試,請記住,如果您看到一隻眼睛,那就是其他人透過網路在某個地方看著您! </p>


          </div>

            
        

以上是使用 Supabase 和 WebGazer.js 建立即時眼動追蹤體驗的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
node.js流帶打字稿node.js流帶打字稿Apr 30, 2025 am 08:22 AM

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE

Python vs. JavaScript:性能和效率注意事項Python vs. JavaScript:性能和效率注意事項Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

JavaScript的起源:探索其實施語言JavaScript的起源:探索其實施語言Apr 29, 2025 am 12:51 AM

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

幕後:什麼語言能力JavaScript?幕後:什麼語言能力JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來:趨勢和預測Python和JavaScript的未來:趨勢和預測Apr 27, 2025 am 12:21 AM

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python vs. JavaScript:開發環境和工具Python vs. JavaScript:開發環境和工具Apr 26, 2025 am 12:09 AM

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境