{"id":5785,"date":"2026-02-01T10:00:24","date_gmt":"2026-02-01T17:00:24","guid":{"rendered":"https:\/\/rec0m88.com\/?p=5785"},"modified":"2026-02-02T10:58:00","modified_gmt":"2026-02-02T17:58:00","slug":"optimizing-the-game-loop-the-battle-for-frame-pacing-in-the-browser","status":"publish","type":"post","link":"https:\/\/rec0m88.com\/de\/optimizing-the-game-loop-the-battle-for-frame-pacing-in-the-browser\/","title":{"rendered":"Die Optimierung der Spielschleife: Der Kampf um das Frame-Pacing im Browser"},"content":{"rendered":"<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Inhaltsverzeichnis<\/h2><nav><ul><li><a href=\"#the-v-sync-ghost-why-60-hz-isnt-always-60-hz\">Der V-Sync-Ghost: Warum 60 Hz nicht immer 60 Hz sind<\/a><ul><li><a href=\"#our-solution-request-animation-frame-and-the-web-audio-clock\">Unsere L\u00f6sung: `requestAnimationFrame` und die Web-Audio-Uhr<\/a><\/li><\/ul><\/li><li><a href=\"#the-audio-crackle-solving-the-buffer-underflow\">Das Knacken im Ton: Behebung des \u201cPuffer-Unterlaufs\u201d<\/a><\/li><li><a href=\"#managing-jitter-in-a-jit-world\">Umgang mit Jitter in einer JIT-Welt<\/a><\/li><li><a href=\"#the-input-to-photon-latency\">Die Latenzzeit vom Eingangssignal bis zum Photon<\/a><\/li><li><a href=\"#why-high-value-sites-care-about-timing\">Warum hochkar\u00e4tige Websites Wert auf das richtige Timing legen<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr den fl\u00fcchtigen Betrachter ist ein Spiel lediglich eine Abfolge von bewegten Bildern. F\u00fcr einen Entwickler hingegen ist ein Spiel eine unerbittliche Hochgeschwindigkeitsuhr. Bei Retro-Emulatoren und HTML5-Arcade-Titeln ist diese Uhr gnadenlos. Wenn das Timing auch nur um wenige Millisekunden verrutscht, knistert der Ton, das Scrollen \u201cruckelt\u201d und das Gameplay wirkt tr\u00e4ge.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bei <strong>Rec0m88<\/strong>, die gr\u00f6\u00dfte technische H\u00fcrde besteht nicht nur darin, das Spiel zum Laufen zu bringen \u2013 sondern darin, es zu meistern <strong>Frame-Taktung<\/strong>. JavaScript wurde urspr\u00fcnglich f\u00fcr das Scrollen von Text und das Klicken auf Schaltfl\u00e4chen entwickelt, nicht f\u00fcr die Verwaltung der Sub-Millisekunden-Genauigkeit, die eine Arcade-Platine aus den 1990er Jahren erfordert. Hier erfahren Sie, wie wir die Engine des Browsers so gez\u00e4hmt haben, dass sie ein fl\u00fcssiges, konstantes Gameplay mit 60 FPS liefert.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-v-sync-ghost-why-60-hz-isnt-always-60-hz\">Der V-Sync-Ghost: Warum 60 Hz nicht immer 60 Hz sind<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Die meisten Retro-Konsolen wurden f\u00fcr NTSC-Fernseher entwickelt, die mit genau <strong>59,94 Hz<\/strong>. Moderne Computermonitore arbeiten jedoch in der Regel mit einer festen <strong>60 Hz<\/strong>, <strong>120 Hz<\/strong>, oder <strong>144 Hz<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn man ein Spiel mit 59,94 Hz auf einem 60-Hz-Monitor ausf\u00fchrt, ger\u00e4t man in einen mathematischen Albtraum. Alle paar Sekunden \u201c\u00fcberspringt\u201d der Monitor ein Bild, weil die Timings nicht perfekt zusammenpassen. Dies f\u00fchrt dazu, dass <strong>Mikro-Ruckeln<\/strong>\u2014dieser nervige \u201cSprung\u201d, den man sieht, wenn eine Figur \u00fcber den Bildschirm l\u00e4uft.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"our-solution-request-animation-frame-and-the-web-audio-clock\">Unsere L\u00f6sung: <code>requestAnimationFrame<\/code> und die Web-Audio-Uhr<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">In einer Standard-Webanwendung verwenden Entwickler <code>setTimeout<\/code> oder <code>setInterval<\/code> um Code auszuf\u00fchren. Diese sind f\u00fcr Spiele zu \u201cunpr\u00e4zise\u201d; sie k\u00f6nnen vom Browser verz\u00f6gert werden, wann immer dieser ausgelastet ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stattdessen verwendet Rec0m88 ein Dual-Takt-System:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Visuelle Synchronisation:<\/strong> Wir verwenden <code>requestAnimationFrame<\/code> (rAF). Damit wird dem Browser mitgeteilt: \u201cF\u00fchre den n\u00e4chsten Frame des Spiels erst dann aus, wenn der Monitor bereit ist, ihn darzustellen.\u201d Dadurch wird \u201cScreen Tearing\u201d vermieden und sichergestellt, dass das Video seidenweich l\u00e4uft.<\/li>\n\n\n\n<li><strong>Audiosynchronisation:<\/strong> Da die Bildwiederholfrequenz des Monitors schwanken kann, verwenden wir die <strong>Zeit im Web-Audio-API-Kontext<\/strong> als unser \u201cMetronom\u201d. Die Web-Audio-Uhr ist der pr\u00e4ziseste Timer im Browser. Indem wir unsere WebAssembly (WASM)-Spielschleife an den Audio-Puffer koppeln, stellen wir sicher, dass der Ton niemals gegen\u00fcber dem Geschehen aus dem Takt ger\u00e4t.<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-audio-crackle-solving-the-buffer-underflow\">Das Knacken im Ton: Behebung des \u201cPuffer-Unterlaufs\u201d<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Hast du schon einmal ein Online-Spiel gespielt, bei dem der Ton knackt oder rauscht, wenn du einen neuen Tab \u00f6ffnest? Das ist ein <strong>Puffer-Unterlauf<\/strong>. Das liegt daran, dass der Hauptthread des Browsers zu sehr ausgelastet war, um der Soundkarte die n\u00e4chsten paar Millisekunden Ton zu \u201cliefern\u201d.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Um dieses Problem in Rec0m88 zu beheben, haben wir die Audioverarbeitung auf einen <strong>Web-Mitarbeiter<\/strong>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Entkoppelte Logik:<\/strong> Die Spielelogik l\u00e4uft auf einem Thread, die Audiomischung auf einem anderen.<\/li>\n\n\n\n<li><strong>Ringpuffer:<\/strong> Wir verwalten einen winzigen \u201cLook-Ahead\u201d-Puffer mit Audio-Samples. Wenn die CPU-Auslastung f\u00fcr eine Millisekunde sprunghaft ansteigt, greift der Audio-Player auf diesen Puffer zur\u00fcck, sodass das System Zeit hat, sich zu erholen, ohne dass der Nutzer jemals ein \u201cKnacken\u201d h\u00f6rt.\u201d<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"managing-jitter-in-a-jit-world\">Umgang mit Jitter in einer JIT-Welt<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">JavaScript ist eine <strong>JIT (Just-in-Time)<\/strong> kompilierte Sprache. Das bedeutet, dass der Browser den Code w\u00e4hrend des Spielens st\u00e4ndig \u201coptimiert\u201d. Manchmal f\u00fchren diese Optimierungen zu einem kurzzeitigen \u201cH\u00e4ngenbleiben\u201d (oft als \u201cJank\u201d bezeichnet).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Um eine perfekte Bildrate zu erreichen, minimieren wir die \u201cGarbage Collection\u201d (GC). Bei der GC h\u00e4lt der Browser alle Vorg\u00e4nge an, um nicht genutzten Speicher freizugeben. Wir vermeiden dies durch:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Objekt-Pooling:<\/strong> Wir verwenden dieselben Speicherbl\u00f6cke f\u00fcr Spiel-Sprites und P2P-Pakete wieder, anstatt neue anzulegen.<\/li>\n\n\n\n<li><strong>Typisierte Arrays:<\/strong> Wir verwenden <code>Uint8Array<\/code> und <code>Float32Array<\/code> zur Verarbeitung von Spieldaten. Diese werden in einem fest zugewiesenen Speicherbereich gespeichert, den der Browser nicht so oft \u201cbereinigen\u201d muss, wodurch die Spielschleife vorhersehbar bleibt.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"the-input-to-photon-latency\">Die Latenzzeit vom Eingangssignal bis zum Photon<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u201cBei \u201dFrame Pacing\u201c geht es nicht nur darum, was man sieht, sondern auch darum, was man sp\u00fcrt. Die Zeit zwischen dem Dr\u00fccken einer Taste und dem Farbwechsel eines Pixels wird als <strong>Latenzzeit vom Eingangssignal bis zum Photon<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Durch die Verwendung der <strong>Web-HID-API<\/strong> und die <strong>Gamepad-API<\/strong>, umgehen wir die standardm\u00e4\u00dfige \u201cKeyboard Event\u201d-Warteschlange des Browsers. Dadurch k\u00f6nnen wir Ihren Controller zu Beginn jedes Frames abfragen, unmittelbar bevor der WASM-Kern ausgef\u00fchrt wird. So wird sichergestellt, dass Ihre Eingaben in der <em>gleich danach<\/em> Rahmen, der das \u201cspritzige\u201d Spielgef\u00fchl vermittelt, das Arcade-Fans erwarten.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"why-high-value-sites-care-about-timing\">Warum hochkar\u00e4tige Websites Wert auf das richtige Timing legen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Der \u201cLow Value Content\u201d-Filter von Google ber\u00fccksichtigt nicht nur den Text, sondern auch die <strong>Benutzererfahrung<\/strong>. Eine Website, die einen ruckelnden, stockenden Emulator bereitstellt, ist ein Tool \u201cminderer Qualit\u00e4t\u201d. Indem wir unsere Herausforderungen beim Frame-Pacing dokumentieren und eine hochpr\u00e4zise Engine bereitstellen, zeigen wir, dass Rec0m88 eine Plattform auf professionellem Niveau ist.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wir haben Hunderte von Stunden damit verbracht, das Zusammenspiel zwischen der Web Audio API, WebAssembly und der GPU zu optimieren, um sicherzustellen, dass die Wiedergabe eines Klassikers auf unserer Plattform nicht nur funktioniert, sondern sich auch richtig anf\u00fchlt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Die Meta-Herausforderung:<\/strong> \u00d6ffnen Sie doch einmal den \u201cLeistungsmonitor\u201d Ihres Browsers, w\u00e4hrend Sie eines unserer Spiele spielen. Sie werden eine gleichm\u00e4\u00dfige, konstante Linie bei 60 FPS sehen \u2013 ein Beweis f\u00fcr die Leistungsf\u00e4higkeit unserer Frame-Pacing-Architektur.<\/p>","protected":false},"excerpt":{"rendered":"<p>F\u00fcr den fl\u00fcchtigen Betrachter ist ein Spiel lediglich eine Abfolge von bewegten Bildern. F\u00fcr einen Entwickler hingegen ist ein Spiel eine unerbittliche Hochgeschwindigkeitsuhr. F\u00fcr Retro-Emulatoren und HTML5-Arcade-Titel ist diese Uhr gnadenlos. Wenn das Timing auch nur um wenige Millisekunden verrutscht, knistert der Ton, das Scrollen \u201cruckelt\u201d und das Gameplay \u2026<\/p>","protected":false},"author":1,"featured_media":5764,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpai_generated_summary":"","_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"categories":[40],"tags":[],"class_list":["post-5785","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-meta"],"_links":{"self":[{"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/posts\/5785","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/comments?post=5785"}],"version-history":[{"count":0,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/posts\/5785\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/media\/5764"}],"wp:attachment":[{"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/media?parent=5785"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/categories?post=5785"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rec0m88.com\/de\/wp-json\/wp\/v2\/tags?post=5785"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}