


Erstellen Sie mit Supabase und WebGazer.js ein Echtzeit-Eye-Tracking-Erlebnis
TL;DR:
- Erstellt mit Supabase, React, WebGazer.js, Motion One, anime.js, Stable Audio
- Nutzt Supabase Realtime Presence & Broadcast (es werden überhaupt keine Datenbanktabellen verwendet!)
- GitHub-Repo
- Website
- Demovideo
Noch ein weiterer Supabase Launch Week Hackathon und ein weiteres experimentelles Projekt namens Gaze into the Abyss. Dies war letztendlich eines der einfachsten und komplexesten Projekte zugleich. Zum Glück hat mir Cursor in letzter Zeit ziemlich viel Spaß gemacht, sodass ich einige helfende Hände dabei hatte, es durchzustehen! Ich wollte auch eine Frage in meinem Kopf validieren: Ist es möglich, nur die Echtzeitfunktionen von Supabase ohne Datenbanktabellen zu verwenden? Die (vielleicht etwas offensichtliche) Antwort lautet: Ja, ja (liebe Grüße, Realtime-Team ♥️). Tauchen wir also etwas tiefer in die Umsetzung ein.
Die Idee
Eines Tages dachte ich zufällig an Nietzsches Zitat über den Abgrund und dass es schön (und cool) wäre, es sich tatsächlich irgendwie vorzustellen: Man starrt auf einen dunklen Bildschirm und etwas starrt zurück. Mehr ist da nicht drin!
Aufbau des Projekts
Anfangs hatte ich die Idee, Three.js zu verwenden, um dieses Projekt zu erstellen, mir wurde jedoch klar, dass dies bedeuten würde, dass ich einige kostenlose Assets für die 3D-Augen erstellen oder finden müsste. Ich entschied, dass es etwas zu viel ist, vor allem, weil ich nicht viel Zeit hatte, um am Projekt selbst zu arbeiten, und habe mich stattdessen dafür entschieden, es in 2D mit SVGs zu machen.
Ich wollte auch nicht, dass es nur visuell ist: Mit etwas Audio wäre es auch ein besseres Erlebnis. Deshalb kam mir die Idee, dass es großartig wäre, wenn die Teilnehmer über ein Mikrofon sprechen könnten und andere es als unberechtigtes Flüstern oder vorbeiziehenden Wind hören könnten. Dies erwies sich jedoch als sehr herausfordernd und ich beschloss, es ganz aufzugeben, da ich WebAudio und WebRTC nicht gut miteinander verbinden konnte. Ich habe eine übrig gebliebene Komponente in der Codebasis, die auf das lokale Mikrofon hört und „Windgeräusche“ für den aktuellen Benutzer auslöst, falls Sie einen Blick darauf werfen möchten. Vielleicht gibt es in Zukunft noch etwas hinzuzufügen?
Echtzeiträume
Bevor ich an visuellen Dingen arbeitete, wollte ich das Echtzeit-Setup testen, das ich mir vorgestellt hatte. Da es bei der Echtzeitfunktion einige Einschränkungen gibt, wollte ich, dass sie so funktioniert:
- Es sind max. 10 Teilnehmer gleichzeitig in einem Kanal
- Das bedeutet, dass Sie einem neuen Kanal beitreten müssen, wenn einer voll ist
- Sie sollten nur die Augen anderer Teilnehmer sehen
Dazu habe ich mir ein useEffect-Setup ausgedacht, bei dem es rekursiv mit einem Echtzeitkanal verknüpft wird, etwa so:
Dieser joinRoom befindet sich im useEffect-Hook und wird aufgerufen, wenn die Raumkomponente gemountet wird. Eine Einschränkung, die ich bei der Arbeit an dieser Funktion festgestellt habe, war, dass der Parameter „currentPresences“ keine Werte im Join-Ereignis enthält, obwohl er verfügbar ist. Ich bin mir nicht sicher, ob es sich um einen Fehler in der Implementierung handelt oder ob es wie vorgesehen funktioniert. Daher muss ein manueller Abruf von „room.presenceState“ durchgeführt werden, um die Anzahl der Teilnehmer im Raum zu ermitteln, wann immer der Benutzer beitritt.
Wir überprüfen die Teilnehmerzahl und melden uns entweder vom aktuellen Raum ab und versuchen, einem anderen Raum beizutreten, oder fahren dann mit dem aktuellen Raum fort. Wir tun dies im Beitrittsereignis, da die Synchronisierung zu spät wäre (sie wird nach Beitritts- oder Austrittsereignissen ausgelöst).
Ich habe diese Implementierung getestet, indem ich eine ganze Reihe von Tabs in meinem Browser geöffnet habe, und alles schien gut zu sein!
Danach wollte ich die Lösung mit Mauspositionsaktualisierungen debuggen und stieß schnell auf einige Probleme beim Senden zu vieler Nachrichten im Kanal! Die Lösung: Anrufe drosseln.
/** * 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 hat sich diesen kleinen Drosselfunktions-Ersteller ausgedacht und ich habe ihn für die Eye-Tracking-Übertragungen wie folgt verwendet:
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 })
Das hat sehr geholfen! Außerdem habe ich in den ersten Versionen die Eye-Tracking-Nachrichten mit Präsenz gesendet, Broadcast erlaubt jedoch mehr Nachrichten pro Sekunde, also habe ich die Implementierung stattdessen darauf umgestellt. Dies ist besonders wichtig beim Eye-Tracking, da die Kamera ständig alles aufzeichnet.
Blickverfolgung
Ich bin vor einiger Zeit auf WebGazer.js gestoßen, als ich zum ersten Mal die Idee für dieses Projekt hatte. Es ist ein sehr interessantes Projekt und funktioniert überraschend gut!
Die gesamten Eye-Tracking-Funktionen werden in einer Funktion in einem useEffect-Hook ausgeführt:
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) } })
Das Ermitteln der Informationen dort, wo der Benutzer gerade hinschaut, ist einfach und funktioniert wie das Ermitteln der Mauspositionen auf dem Bildschirm. Allerdings wollte ich auch die Blinzelerkennung als (coole) Funktion hinzufügen, was einige Hürden erforderte.
Wenn Sie Informationen zu WebGazer und Blinzelerkennung googeln, können Sie einige Überreste einer ersten Implementierung sehen. Es gibt sogar auskommentierten Code in der Quelle. Leider sind diese Funktionen in der Bibliothek nicht verfügbar. Sie müssen dies manuell tun.
Nach vielen Versuchen konnten Cursor und ich eine Lösung entwickeln, die Pixel und Helligkeitsstufen aus den Augenklappendaten berechnet, um festzustellen, wann der Benutzer blinzelt. Es gibt auch einige dynamische Beleuchtungsanpassungen, da mir aufgefallen ist, dass die Webcam (zumindest für mich) je nach Beleuchtung nicht immer erkennt, wann Sie blinzeln. Bei mir funktionierte es schlechter, je heller mein Bild/Raum war, und besser bei dunklerer Beleuchtung (siehe Abbildung).
Beim Debuggen der Eye-Tracking-Funktionen (WebGazer verfügt über einen sehr schönen .setPredictionPoints-Aufruf, der einen roten Punkt auf dem Bildschirm anzeigt, um zu visualisieren, wohin Sie schauen) ist mir aufgefallen, dass das Tracking nicht sehr genau ist es sei denn, Sie kalibrieren es. Dazu werden Sie im Projekt aufgefordert, bevor Sie einem Raum beitreten.
/** * 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>
Es war eine sehr coole Erfahrung, dies in Aktion zu sehen! Ich habe den gleichen Ansatz auf die umgebenden Linien angewendet und den Cursor angewiesen, sie zur Mitte hin zu „kollabieren“: was praktisch auf einmal geschah!
Die Augen würden dann in einem einfachen CSS-Raster gerendert, wobei die Zellen so ausgerichtet sind, dass ein vollständiger Raum wie ein großes Auge aussieht.
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 })
Letzte Handgriffe
Dann legen Sie noch einen schönen Einführungsbildschirm und Hintergrundmusik ein und das Projekt kann losgehen!
Audio verbessert immer das Erlebnis, wenn man an solchen Dingen arbeitet, deshalb habe ich Stable Audio verwendet, um eine Hintergrundmusik zu erzeugen, wenn der Benutzer „den Abgrund betritt“. Die Eingabeaufforderung, die ich für die Musik verwendet habe, war folgende:
Ambiente, gruselig, Hintergrundmusik, flüsternde Geräusche, Winde, langsames Tempo, unheimlich, Abgrund
Ich fand auch, dass ein einfacher schwarzer Bildschirm etwas langweilig ist, also habe ich ein paar animierte SVG-Filtersachen im Hintergrund hinzugefügt. Zusätzlich habe ich einen dunklen, unscharfen Kreis in der Mitte des Bildschirms hinzugefügt, um einen schönen Fade-Effekt zu erzielen. Ich hätte das wahrscheinlich mit SVG-Filtern machen können, aber ich wollte nicht zu viel Zeit damit verbringen. Um noch mehr Bewegung zu haben, habe ich den Hintergrund um seine Achse drehen lassen. Manchmal ist das Erstellen von Animationen mit den SVG-Filtern etwas kompliziert, deshalb habe ich mich stattdessen für diese Methode entschieden.
<div> <h2> Abschluss </h2> <p>Da haben Sie es also: Ein ziemlich einfacher Einblick in die Implementierung eines stilisierten Eye-Trackings mit den Echtzeitfunktionen von Supabase. Ich persönlich fand das ein sehr interessantes Experiment und hatte bei der Arbeit nicht allzu viele Probleme. Und überraschenderweise musste ich die letzte Nacht nicht durcharbeiten, bevor ich das Projekt einreichte!</p> <p>Schauen Sie sich gerne das Projekt oder das Demo-Video an, wie es entstanden ist. Es kann zu Problemen kommen, wenn viele Leute es gleichzeitig verwenden (sehr schwer zu testen, da mehrere Geräte und Webcams erforderlich sind, um es richtig zu machen), aber ich denke, das ist bei Hackathon-Projekten üblich? Und wenn Sie es ausprobieren, denken Sie daran: Wenn Sie ein Auge sehen, ist es jemand anderes, der Sie irgendwo im Internet beobachtet!</p> </div>
Das obige ist der detaillierte Inhalt vonErstellen Sie mit Supabase und WebGazer.js ein Echtzeit-Eye-Tracking-Erlebnis. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

JavaScript stammt aus dem Jahr 1995 und wurde von Brandon Ike erstellt und realisierte die Sprache in C. 1.C-Sprache bietet Programmierfunktionen auf hoher Leistung und Systemebene für JavaScript. 2. Die Speicherverwaltung und die Leistungsoptimierung von JavaScript basieren auf C -Sprache. 3. Die plattformübergreifende Funktion der C-Sprache hilft JavaScript, auf verschiedenen Betriebssystemen effizient zu laufen.

JavaScript wird in Browsern und Node.js -Umgebungen ausgeführt und stützt sich auf die JavaScript -Engine, um Code zu analysieren und auszuführen. 1) abstrakter Syntaxbaum (AST) in der Parsenstufe erzeugen; 2) AST in die Kompilierungsphase in Bytecode oder Maschinencode umwandeln; 3) Führen Sie den kompilierten Code in der Ausführungsstufe aus.

Zu den zukünftigen Trends von Python und JavaScript gehören: 1. Python wird seine Position in den Bereichen wissenschaftlicher Computer und KI konsolidieren. JavaScript wird die Entwicklung der Web-Technologie fördern. Beide werden die Anwendungsszenarien in ihren jeweiligen Bereichen weiter erweitern und mehr Durchbrüche in der Leistung erzielen.

Sowohl Python als auch JavaScripts Entscheidungen in Entwicklungsumgebungen sind wichtig. 1) Die Entwicklungsumgebung von Python umfasst Pycharm, Jupyternotebook und Anaconda, die für Datenwissenschaft und schnelles Prototyping geeignet sind. 2) Die Entwicklungsumgebung von JavaScript umfasst Node.JS, VSCODE und WebPack, die für die Entwicklung von Front-End- und Back-End-Entwicklung geeignet sind. Durch die Auswahl der richtigen Tools nach den Projektbedürfnissen kann die Entwicklung der Entwicklung und die Erfolgsquote der Projekte verbessert werden.

Ja, der Motorkern von JavaScript ist in C. 1) Die C -Sprache bietet eine effiziente Leistung und die zugrunde liegende Steuerung, die für die Entwicklung der JavaScript -Engine geeignet ist. 2) Die V8-Engine als Beispiel wird sein Kern in C geschrieben, wobei die Effizienz und objektorientierte Eigenschaften von C kombiniert werden.

JavaScript ist das Herzstück moderner Websites, da es die Interaktivität und Dynamik von Webseiten verbessert. 1) Es ermöglicht die Änderung von Inhalten, ohne die Seite zu aktualisieren, 2) Webseiten durch DOMAPI zu manipulieren, 3) Komplexe interaktive Effekte wie Animation und Drag & Drop, 4) die Leistung und Best Practices optimieren, um die Benutzererfahrung zu verbessern.

C und JavaScript erreichen die Interoperabilität durch WebAssembly. 1) C -Code wird in das WebAssembly -Modul zusammengestellt und in die JavaScript -Umgebung eingeführt, um die Rechenleistung zu verbessern. 2) In der Spieleentwicklung kümmert sich C über Physik -Engines und Grafikwiedergabe, und JavaScript ist für die Spiellogik und die Benutzeroberfläche verantwortlich.

JavaScript wird in Websites, mobilen Anwendungen, Desktop-Anwendungen und serverseitigen Programmierungen häufig verwendet. 1) In der Website -Entwicklung betreibt JavaScript DOM zusammen mit HTML und CSS, um dynamische Effekte zu erzielen und Frameworks wie JQuery und React zu unterstützen. 2) Durch reaktnatives und ionisches JavaScript wird ein plattformübergreifendes mobile Anwendungen entwickelt. 3) Mit dem Elektronenframework können JavaScript Desktop -Anwendungen erstellen. 4) Node.js ermöglicht es JavaScript, auf der Serverseite auszuführen und unterstützt hohe gleichzeitige Anforderungen.


Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

EditPlus chinesische Crack-Version
Geringe Größe, Syntaxhervorhebung, unterstützt keine Code-Eingabeaufforderungsfunktion

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

WebStorm-Mac-Version
Nützliche JavaScript-Entwicklungstools

ZendStudio 13.5.1 Mac
Leistungsstarke integrierte PHP-Entwicklungsumgebung

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)
