<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kehinde Owolabi</title>
    <description>The latest articles on DEV Community by Kehinde Owolabi (@kehinde_owolabi_e2e54567a).</description>
    <link>https://dev.to/kehinde_owolabi_e2e54567a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3480586%2F6714a0bc-49c6-40be-bf45-1be2cb3ba21a.jpg</url>
      <title>DEV Community: Kehinde Owolabi</title>
      <link>https://dev.to/kehinde_owolabi_e2e54567a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kehinde_owolabi_e2e54567a"/>
    <language>en</language>
    <item>
      <title>🚀 From FlipaClip to SitePoint: The Full Story of Kehinde Owolabi</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Mon, 24 Aug 2026 03:43:40 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/from-flipaclip-to-sitepoint-the-full-story-of-kehinde-owolabi-2eb</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/from-flipaclip-to-sitepoint-the-full-story-of-kehinde-owolabi-2eb</guid>
      <description>&lt;h1&gt;
  
  
  🚀 From FlipaClip to SitePoint: The Full Story of Kehinde Owolabi
&lt;/h1&gt;

&lt;h2&gt;
  
  
  How a Nigerian teenager built a professional game engine with borrowed laptops, offline W3Schools, and pure determination.
&lt;/h2&gt;




&lt;h2&gt;
  
  
  🎮 Play the Game
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://limn-engine-doc.vercel.app/test9.html" rel="noopener noreferrer"&gt;Try Limn Engine Live — Space Shooter Demo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;See what 4 years of determination built. This space shooter runs at 60 FPS on a Tecno Pop 4 with 1GB RAM.&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Every developer has an origin story. Some start with a fancy computer and a computer science degree. Others start with a flipbook app and a sister who trusted them with her phone.&lt;/p&gt;

&lt;p&gt;My name is &lt;strong&gt;Kehinde Owolabi&lt;/strong&gt;. I'm 18 years old (born December 4, 2007), and I live in Lagos, Nigeria. I'm currently in PC103 at BYU Pathway, and I'm a member of The Church of Jesus Christ of Latter-day Saints.&lt;/p&gt;

&lt;p&gt;I built a &lt;strong&gt;94/100 professional game engine&lt;/strong&gt; called Limn Engine. It runs at 60 FPS on a Toshiba with 4GB RAM. It was published on SitePoint and ranked #3 among 2D JavaScript game engines.&lt;/p&gt;

&lt;p&gt;Nobody knew it was developed on a Chromebook, a borrowed Thinkpad (behind my sister's back), and a Toshiba that "hung like hell." That was the secret I kept for months.&lt;/p&gt;

&lt;p&gt;But that's only one part of this story.&lt;/p&gt;

&lt;p&gt;This is the full story of how I went from a button phone to a 94/100 game engine, from FlipaClip to SitePoint, from a boy who failed physics to a developer who built something that runs on a Tecno Pop 4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The one-line summary:&lt;/strong&gt; &lt;em&gt;"I'm Kehinde Owolabi, an 18-year-old developer from Lagos, Nigeria who went from FlipaClip to building a 94/100 game engine on borrowed laptops — and got published on SitePoint."&lt;/em&gt; 🎮🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 The Beginning: FlipaClip and the Spark of Creativity
&lt;/h2&gt;

&lt;p&gt;Before I was a developer, I was an animator.&lt;/p&gt;

&lt;p&gt;I used &lt;strong&gt;FlipaClip&lt;/strong&gt; — a simple animation app on mobile — to create flipbook-style animations. I loved bringing characters to life, frame by frame. I would spend hours drawing, tweaking, and watching my creations move.&lt;/p&gt;

&lt;p&gt;That creative spark stayed with me. I wanted to create interactive experiences. I wanted to build games. But I didn't know how.&lt;/p&gt;

&lt;p&gt;At that time, I didn't even know what programming was. I just knew I wanted to make things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Animation taught me the value of frame-by-frame iteration — a skill that later helped me debug game loops and rendering pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 The First Encounter with Code
&lt;/h2&gt;

&lt;p&gt;When I was in SS1 (Senior Secondary 1), I opened my computer textbook and found something unexpected: a &lt;strong&gt;BASIC tutorial&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I read it cover to cover. I was fascinated by the idea that you could type words and make a computer do things. It felt like magic — but it was real. It was something I could learn.&lt;/p&gt;

&lt;p&gt;But there was a problem: I didn't have a phone or a laptop.&lt;/p&gt;

&lt;p&gt;I couldn't practice. I couldn't test the code. I could only read and imagine what it would be like to actually run the programs. I wrote down code snippets in my notebook, hoping that someday I would have a device to try them on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; You don't need a computer to start learning. I wrote code on paper before I ever typed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  📱 The Turning Point: A Sister's Phone and a Love for Python
&lt;/h2&gt;

&lt;p&gt;Then came physics class.&lt;/p&gt;

&lt;p&gt;I was failing physics — not because I didn't understand it, but because I was absent from class. When I returned, I had fallen behind. My sister noticed and gave me her phone so I could read and research.&lt;/p&gt;

&lt;p&gt;Instead of just studying physics, I started researching programming.&lt;/p&gt;

&lt;p&gt;I discovered &lt;strong&gt;Python&lt;/strong&gt;. I read about it. I watched videos about it. And I fell in love.&lt;/p&gt;

&lt;p&gt;Python was simple. It was readable. It made sense in a way that physics didn't at the time. I knew I had to learn it. I spent hours reading tutorials, watching YouTube videos, and absorbing everything I could.&lt;/p&gt;

&lt;p&gt;That's when I realized: I was genuinely interested in coding. It wasn't just a distraction from physics — it was something I wanted to pursue.&lt;/p&gt;

&lt;p&gt;I still love physics, by the way. I only failed because I was absent. Once I caught up, I understood it just fine. But coding had already captured my heart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Sometimes your biggest failure becomes your biggest opportunity. Physics led me to coding.&lt;/p&gt;




&lt;h2&gt;
  
  
  📲 The Button Phone Era
&lt;/h2&gt;

&lt;p&gt;Before my sister got the laptop, I used my mom's &lt;strong&gt;Itel button phone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Data was available sometimes. I would Google codes and write them down on paper — because I couldn't save files on a button phone.&lt;/p&gt;

&lt;p&gt;When my sister's friend came around, I would borrow their phone and go to Webmaker to practice.&lt;/p&gt;

&lt;p&gt;I learned to code without a laptop. That's not a flex — that's survival. I did whatever it took to keep learning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Resourcefulness is more important than resources.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 The JavaScript Journey
&lt;/h2&gt;

&lt;p&gt;In 2023, I was asked to build a website for a competition in &lt;strong&gt;Public Art Africa&lt;/strong&gt;. I built it on Replit using HTML, CSS, and JavaScript.&lt;/p&gt;

&lt;p&gt;That's when I learned JavaScript. Not because I planned to — but because I had to. The competition required a website, and JavaScript was the language of the web.&lt;/p&gt;

&lt;p&gt;After the competition, I wanted to build games. I knew about Pygame, but JavaScript worked in the browser — no installation needed, no complex setup. I could just open a tab and start coding.&lt;/p&gt;

&lt;p&gt;That was the hook. JavaScript gave me the freedom to create without barriers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Sometimes the best way to learn is to be thrown into the deep end. I learned JavaScript because I had to ship a project.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖥️ The Laptop Heist Era
&lt;/h2&gt;

&lt;p&gt;I didn't have a laptop of my own. Not for a long time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sister's Toshiba (4GB RAM — "Hangs Like Hell")
&lt;/h3&gt;

&lt;p&gt;My sister got a Toshiba laptop with 4GB of RAM. It hung like hell. But I coded on it anyway. It didn't matter that it was slow — it was a laptop, and I could use it to build things.&lt;/p&gt;

&lt;p&gt;The problem? She wouldn't give it to me willingly. I had to code &lt;strong&gt;behind her back&lt;/strong&gt;. If anything happened to the laptop, she would blame me.&lt;/p&gt;

&lt;p&gt;Every time I heard her footsteps, I would quickly close the lid. I coded in secret, in fear, in stolen moments. But I coded.&lt;/p&gt;

&lt;p&gt;The charger spoiled. We didn't buy a new one for two months. But I didn't stop coding. I found other ways — borrowing phones, writing code on paper, waiting for opportunities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Fear of being caught couldn't stop me. If you really want to build something, you'll find a way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brother's HP (He Let Me)
&lt;/h3&gt;

&lt;p&gt;My brother &lt;strong&gt;David&lt;/strong&gt; got a laptop later. He let me code on it. No guilt. No hiding. Just permission.&lt;/p&gt;

&lt;p&gt;That HP saw &lt;strong&gt;TCJSGame V3&lt;/strong&gt;. The breakthrough. The Tile Class. The flow state. For the first time, I could code without looking over my shoulder.&lt;/p&gt;

&lt;p&gt;David's support meant everything. He believed in me when I didn't have much to show.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Permission and support are powerful. One person believing in you can change everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 The Thinkpad Era (Behind My Sister's Back Again)
&lt;/h2&gt;

&lt;p&gt;After the Toshiba spoiled, I had to use my sister's Thinkpad — again behind her back.&lt;/p&gt;

&lt;p&gt;That Thinkpad saw &lt;strong&gt;V2&lt;/strong&gt; of the engine.&lt;/p&gt;

&lt;p&gt;I renamed it to &lt;strong&gt;TCgame&lt;/strong&gt;, then &lt;strong&gt;TCJSgame&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Copilot helped clean up the code and organize everything into classes. It was the first time the engine looked like a real project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Even when you're building in secret, you can still build something real.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 The Church WiFi
&lt;/h2&gt;

&lt;p&gt;My chapel is the &lt;strong&gt;Agege Stake Center&lt;/strong&gt;, and Abule Egba Ward. There's WiFi there.&lt;/p&gt;

&lt;p&gt;So when I was in church, I would download:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YouTube videos&lt;/li&gt;
&lt;li&gt;Offline W3Schools&lt;/li&gt;
&lt;li&gt;PDFs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I learned Python, Java, JS, HTML, CSS, PHP, Scratch — everything I could. I was hungry for knowledge, and I took it wherever I could find it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Education is available if you're willing to look for it. Even church WiFi can be a classroom.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ The Birth of JGame
&lt;/h2&gt;

&lt;p&gt;I wanted to build games. I started with Pygame but didn't understand it.&lt;/p&gt;

&lt;p&gt;So I decided to build my own engine by copying codes and learning how they worked.&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;JGame&lt;/strong&gt; — the first version of what would eventually become Limn Engine.&lt;/p&gt;

&lt;p&gt;JGame existed on the Toshiba laptop. I finished it but never published it.&lt;/p&gt;

&lt;p&gt;Then the Toshiba spoiled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Your first version doesn't need to be perfect. It just needs to exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤖 The AI Analysis Era
&lt;/h2&gt;

&lt;p&gt;I began analyzing TCJSgame with &lt;strong&gt;DeepSeek&lt;/strong&gt; and &lt;strong&gt;ChatGPT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I knew something was missing. So I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scene management&lt;/li&gt;
&lt;li&gt;TileMap&lt;/li&gt;
&lt;li&gt;hide() and show()&lt;/li&gt;
&lt;li&gt;Gradients&lt;/li&gt;
&lt;li&gt;Fullscreen support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I developed all of this without AI — I just used AI to analyze what was wrong. I wanted to learn how to build things myself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; AI is a tool for analysis, not a replacement for understanding. I built the features myself; AI just helped me see what was missing.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 The Dual-Renderer Breakthrough
&lt;/h2&gt;

&lt;p&gt;Limn Engine V3 had low ratings because of performance.&lt;/p&gt;

&lt;p&gt;I developed extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sonic.js&lt;/strong&gt; (performance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tctxt&lt;/strong&gt; (text rendering)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I developed the &lt;strong&gt;dual-renderer system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea came from tile caching — but the first version had a bug: it rerendered every frame despite caching.&lt;/p&gt;

&lt;p&gt;I fixed it. The dual-renderer was born.&lt;/p&gt;

&lt;p&gt;The engine went from &lt;strong&gt;4 FPS to 60 FPS&lt;/strong&gt; on a Toshiba with 4GB RAM. That's a &lt;strong&gt;15x improvement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Sometimes the problem isn't that something is slow — it's that you're doing it too many times. Cache everything you can.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 The Delta Time Bug
&lt;/h2&gt;

&lt;p&gt;The original code used &lt;code&gt;1 / fps&lt;/code&gt; calculated every second. The problem? The FPS counter resets every second, so at the start of each second, the FPS would start at 0 and build up to 60. This made the game spike at the beginning of every second.&lt;/p&gt;

&lt;p&gt;I tested the fix on my sister's HP laptop without her knowing. The Pygame-style method — calculating the time difference between frames — worked perfectly and eliminated the spikes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; The simplest bugs can have the biggest impact. Delta time is critical for consistent performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  📱 The Tecno Pop 4 Test
&lt;/h2&gt;

&lt;p&gt;Tecno Pop 4 is my mom's phone. 1GB RAM. Weak processor.&lt;/p&gt;

&lt;p&gt;I tested the engine on it.&lt;/p&gt;

&lt;p&gt;At first, it was laggy. The FPS was low. But after fixing the delta time bug and optimizing the renderer, it started working smoothly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If it works on a Tecno Pop 4, it works on anything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That moment when I saw my game running smoothly on that phone — I was proud. Like, "Ha, I actually did this."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Test on the worst hardware you can find. If it works there, it'll work everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏷️ The Rebrand: TCJSgame → Limn Engine
&lt;/h2&gt;

&lt;p&gt;TCJSgame was a working name. Technical. Hard to spell. No meaning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limn&lt;/strong&gt; (verb) — to draw, paint, or portray.&lt;/p&gt;

&lt;p&gt;Perfect. The engine helps you draw games into existence.&lt;/p&gt;

&lt;p&gt;The name change was also practical: TCJSgame was given because all other names (TCgame, TC Games) were already taken. So I chose Limn Engine — a name with meaning, a name that reflects what the engine does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; A name matters. It should tell a story about what you've built.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 The SitePoint Journey
&lt;/h2&gt;

&lt;p&gt;In 2026, I published an article on &lt;strong&gt;SitePoint&lt;/strong&gt; called &lt;em&gt;"Top 10 2D JavaScript Game Engines for Beginners."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine was ranked &lt;strong&gt;#3&lt;/strong&gt; with a &lt;strong&gt;9/10 rating&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Recognition comes when you keep building. I didn't set out to get published — I just kept making things.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 The Rating
&lt;/h2&gt;

&lt;p&gt;Limn Engine received a &lt;strong&gt;94/100&lt;/strong&gt; rating.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ease of Use&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature Set&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Design&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beginner Friendliness&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-Platform&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overall&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94/100&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What this taught me:&lt;/strong&gt; Ratings are nice, but they're not the goal. The goal is to build something people can use.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I'm Kehinde Owolabi, an 18-year-old developer from Lagos, Nigeria who went from FlipaClip to building a 94/100 game engine on borrowed laptops — and got published on SitePoint."&lt;/em&gt; 🎮🚀&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔗 Follow Me
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DEV.to&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Follow Kehinde Owolabi on DEV.to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/terracodes004/limn-engine-doc" rel="noopener noreferrer"&gt;github.com/terracodes004/limn-engine-doc&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discord&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://discord.gg/ZqnUtTQb8" rel="noopener noreferrer"&gt;Join the community&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live Demo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/test9.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/test9.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🐛 Report Bugs
&lt;/h2&gt;

&lt;p&gt;Found a bug? Report it here:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/terracodes004/limn-engine-doc/issues" rel="noopener noreferrer"&gt;github.com/terracodes004/limn-engine-doc/issues&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Upcoming articles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How Limn Engine's Dual-Renderer Works" — The 4 FPS → 60 FPS story&lt;/li&gt;
&lt;li&gt;"Building a Dungeon Crawler with Limn Engine" — Complete game tutorial&lt;/li&gt;
&lt;li&gt;"Limn Engine vs Phaser: Which One Should You Choose?" — Honest comparison&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 What You've Learned
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;How It Applies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resourcefulness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You can learn without a laptop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Persistence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep building, even in secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Render once, paste forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delta time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consistent performance across devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Test on the worst hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚀 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I built a game engine on borrowed laptops, behind my sister's back, with offline W3Schools and church WiFi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If I can do it, so can you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The resources you have are enough. The time you have is enough. The knowledge you have is enough.&lt;/p&gt;

&lt;p&gt;Just keep building.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Draw your game into existence — one frame at a time.&lt;/strong&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>startup</category>
      <category>javascript</category>
      <category>discuss</category>
      <category>showdev</category>
    </item>
    <item>
      <title>🐛 How I Fixed the Delta Time Bug in Limn Engine (And How You Can Too)</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Fri, 21 Aug 2026 03:15:51 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/how-i-fixed-the-delta-time-bug-in-limn-engine-and-how-you-can-too-357</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/how-i-fixed-the-delta-time-bug-in-limn-engine-and-how-you-can-too-357</guid>
      <description>&lt;h1&gt;
  
  
  🐛 How I Fixed the Delta Time Bug in Limn Engine (And How You Can Too)
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A story about frame rates, Chromebooks, and the one bug that broke everything&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Let me tell you about a bug that haunted Limn Engine for months.&lt;/p&gt;

&lt;p&gt;Limn Engine was developed on a &lt;strong&gt;Chromebook&lt;/strong&gt; — but nobody knows that. It was tested on a &lt;strong&gt;Toshiba laptop with 4GB of RAM&lt;/strong&gt;, and everything worked perfectly. Enemies moved at the right speed. The player controlled smoothly. The game ran at a crisp 60 frames per second.&lt;/p&gt;

&lt;p&gt;But when I tested the same game on a &lt;strong&gt;Tecno Pop 4&lt;/strong&gt; (a low-end Android phone with 1GB of RAM and a weak processor), something strange happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The enemies moved at completely different speeds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes they were too fast. Sometimes they were too slow. Sometimes they started fast and then slowed down for no reason.&lt;/p&gt;

&lt;p&gt;The game was broken — and the problem was hiding in plain sight: &lt;strong&gt;delta time&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Is Delta Time (And Why Should You Care?)
&lt;/h2&gt;

&lt;p&gt;Delta time (often written as &lt;code&gt;dt&lt;/code&gt;) is the amount of time that has passed since the last frame. It's measured in &lt;strong&gt;seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Frame Rate&lt;/th&gt;
&lt;th&gt;Delta Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;60 FPS&lt;/td&gt;
&lt;td&gt;~0.0167 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 FPS&lt;/td&gt;
&lt;td&gt;~0.0333 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15 FPS&lt;/td&gt;
&lt;td&gt;~0.0667 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here's the rule: &lt;strong&gt;If you multiply your movement speed by delta time, your game runs at the same speed on all devices.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT: Frame-rate independent&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Moves 200 pixels per second&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ WRONG: Frame-rate dependent&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Moves 4 pixels per frame (speed varies by FPS)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without delta time, your game will run at different speeds on different devices. A fast computer (144 FPS) will feel like the game is in fast-forward, while a slow device (15 FPS) will feel like slow-motion.&lt;/p&gt;




&lt;h2&gt;
  
  
  🐛 The Bug: How I Broke Delta Time
&lt;/h2&gt;

&lt;p&gt;Limn Engine &lt;strong&gt;already had delta time&lt;/strong&gt; — but it was calculated using &lt;code&gt;1 / fps&lt;/code&gt; every second. Here's what the original code looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;refresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked... sort of. But there was a huge problem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The FPS counter resets every second.&lt;/strong&gt; At the start of each second, the FPS starts at 0 and builds up to 60. So for the first fraction of a second, &lt;code&gt;1 / fps&lt;/code&gt; was huge — sometimes &lt;code&gt;0.5&lt;/code&gt; or even &lt;code&gt;1.0&lt;/code&gt;. This made components move incredibly fast at the beginning of each second, then suddenly slow down to normal speed.&lt;/p&gt;

&lt;p&gt;It was like the game was having a seizure every second.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 The Fix: Two Different Approaches
&lt;/h2&gt;

&lt;p&gt;I tested two different fixes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: The &lt;code&gt;1 / fps&lt;/code&gt; Method (Original)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; FPS builds from 0 to 60, causing &lt;code&gt;dt&lt;/code&gt; to spike.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 2: The Pygame Method (What I Ultimately Used)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ani&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeFromAllFrames&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeFromPreviousFrames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeFromPreviousFrames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Convert to seconds&lt;/span&gt;
    &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 The Test: HP Laptop (Behind My Sister's Back)
&lt;/h2&gt;

&lt;p&gt;I tested both approaches on my sister's HP laptop when she wasn't looking (don't worry, she still doesn't know). Here's what I found:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1 / fps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Worked, but had spikes at the start of each second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pygame-style (difference between frames)&lt;/td&gt;
&lt;td&gt;Worked perfectly, smooth movement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Pygame-style method gave similar results but was more reliable.&lt;/strong&gt; It didn't spike at the beginning of each second because it calculates the actual time between frames, not an average over a second.&lt;/p&gt;

&lt;h2&gt;
  
  
  📊 The Results: Before and After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Device&lt;/th&gt;
&lt;th&gt;Before Fix&lt;/th&gt;
&lt;th&gt;After Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chromebook (dev machine)&lt;/td&gt;
&lt;td&gt;Jagged movement&lt;/td&gt;
&lt;td&gt;Perfect speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toshiba Laptop (4GB RAM)&lt;/td&gt;
&lt;td&gt;Game too fast at start&lt;/td&gt;
&lt;td&gt;Perfect speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HP Laptop (tested behind sister's back)&lt;/td&gt;
&lt;td&gt;Spikes every second&lt;/td&gt;
&lt;td&gt;Perfect speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tecno Pop 4 (1GB RAM)&lt;/td&gt;
&lt;td&gt;Game too slow&lt;/td&gt;
&lt;td&gt;Perfect speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop (144 FPS)&lt;/td&gt;
&lt;td&gt;Unplayable&lt;/td&gt;
&lt;td&gt;Perfect speed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The same game now runs at the same speed on every device.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpbljskkq8l68y813fjpn.png" alt=" " width="480" height="960"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🔗 The Code
&lt;/h2&gt;

&lt;p&gt;You can see the final working code here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://github.com/terracodes004/limn-engine-doc/blob/main/asset/epic.js" rel="noopener noreferrer"&gt;Limn Engine Source Code (epic.js)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Look for the &lt;code&gt;ani()&lt;/code&gt; function to see the delta time fix in action.&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 The Documentation
&lt;/h2&gt;

&lt;p&gt;For more information about Limn Engine, check out the complete documentation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://dev.to/kehinde_owolabi_e2e54567a"&gt;Limn Engine Documentation on DEV.to&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🐛 Found a Bug? Report It Here!
&lt;/h2&gt;

&lt;p&gt;If you encounter any issues with Limn Engine, please report them on GitHub:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/terracodes004/limn-engine-doc/issues" rel="noopener noreferrer"&gt;https://github.com/terracodes004/limn-engine-doc/issues&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📋 How to Report a Bug (Helpful Template)
&lt;/h3&gt;

&lt;p&gt;When you open an issue, please include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;A clear description&lt;/strong&gt; of the bug.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Steps to reproduce&lt;/strong&gt; it.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;What you expected&lt;/strong&gt; to happen vs. what actually happened.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Screenshots or error logs&lt;/strong&gt; (if applicable).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Which version&lt;/strong&gt; of Limn Engine you're using.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a template you can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Bug Description:**
[Describe the problem clearly]

**Steps to Reproduce:**
1. Do this...
2. Then do this...
3. Observe the bug...

**Expected Behavior:**
[What should have happened]

**Actual Behavior:**
[What actually happened]

**Screenshots/Logs:**
[Attach if available]

**Limn Engine Version:**
[v0.x.x]

**Device/OS:**
[e.g., Tecno Pop 4, Android 10]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 What I Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;1 / fps&lt;/code&gt; is not the best way to calculate delta time.&lt;/strong&gt; It works, but it's unreliable at the start of each second.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Pygame method is better.&lt;/strong&gt; Calculating the difference between frames is more accurate and doesn't spike.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on low-end devices.&lt;/strong&gt; If it works on a Tecno Pop 4, it'll work on almost anything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test on different laptops.&lt;/strong&gt; I tested on a Chromebook, Toshiba, and HP — and they all showed different issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The dual-renderer saved me.&lt;/strong&gt; Even with the delta time bug, the game was running at 60 FPS on the Toshiba laptop. If I had been using a simpler engine, the performance would have been terrible too.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Develop on a Chromebook.&lt;/strong&gt; Nobody knows Limn Engine was developed on a Chromebook — and now you know my secret.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I fixed a delta time bug that made my game spike every second — and you can too."&lt;/strong&gt; 🎮🚀&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — one frame at a time.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>discuss</category>
      <category>showdev</category>
      <category>github</category>
    </item>
    <item>
      <title>✅❌ The Limn Engine Commandments: 31 Do's and Don'ts for Better Games</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Wed, 19 Aug 2026 21:46:44 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/the-limn-engine-commandments-31-dos-and-donts-for-better-games-3b9g</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/the-limn-engine-commandments-31-dos-and-donts-for-better-games-3b9g</guid>
      <description>&lt;h1&gt;
  
  
  ✅❌ The Limn Engine Commandments: 31 Do's and Don'ts for Better Games
&lt;/h1&gt;




&lt;h2&gt;
  
  
  🎯 Setup &amp;amp; Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ✅ DO: Call &lt;code&gt;display.perform()&lt;/code&gt; Before &lt;code&gt;display.start()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ MUST come first&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;perform()&lt;/code&gt; method is a critical performance patch that replaces the engine's default rendering loop. By default, when you call &lt;code&gt;display.start()&lt;/code&gt;, the engine uses &lt;code&gt;setInterval(() =&amp;gt; this.updat(), 20)&lt;/code&gt; which locks your game to roughly 50 frames per second. This is because &lt;code&gt;setInterval&lt;/code&gt; is unreliable — it doesn't sync with the monitor's refresh rate and can drift over time, causing stutter and inconsistent frame pacing. When you call &lt;code&gt;display.perform()&lt;/code&gt; first, it overrides the &lt;code&gt;start()&lt;/code&gt; method to use &lt;code&gt;requestAnimationFrame()&lt;/code&gt; instead, which synchronizes your game loop with the monitor's refresh rate for smoother animations. More importantly, &lt;code&gt;requestAnimationFrame&lt;/code&gt; pauses automatically when the user switches tabs, saving CPU and battery life. If you accidentally call &lt;code&gt;start()&lt;/code&gt; before &lt;code&gt;perform()&lt;/code&gt;, the &lt;code&gt;setInterval&lt;/code&gt; loop is already running and the patch won't take effect. Always call &lt;code&gt;perform()&lt;/code&gt; first, then &lt;code&gt;start()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Call &lt;code&gt;display.start()&lt;/code&gt; Before &lt;code&gt;display.perform()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Wrong order&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;        &lt;span class="c1"&gt;// ❌ Too late — the damage is done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When you call &lt;code&gt;start()&lt;/code&gt; first, the engine launches the old &lt;code&gt;setInterval&lt;/code&gt; loop that runs at an unreliable ~50fps. The &lt;code&gt;perform()&lt;/code&gt; method works by replacing the &lt;code&gt;start()&lt;/code&gt; method itself with a new version that uses &lt;code&gt;requestAnimationFrame&lt;/code&gt;. However, if &lt;code&gt;start()&lt;/code&gt; has already executed, the damage is done — the &lt;code&gt;setInterval&lt;/code&gt; loop is already running, and the &lt;code&gt;perform()&lt;/code&gt; patch won't stop it or replace it. You'll end up with two loops fighting for control, causing erratic behavior and inconsistent frame rates. The only way to fix this is to refresh the page and call &lt;code&gt;perform()&lt;/code&gt; first. This is one of the most common mistakes beginners make, and it's also one of the hardest to debug because the game appears to run but feels "off" or stuttery. Always remember: &lt;code&gt;perform()&lt;/code&gt; first, then &lt;code&gt;start()&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. ✅ DO: Name Your Display Instance &lt;code&gt;display&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Correct&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The Limn Engine was built with the expectation that your main display instance will be called &lt;code&gt;display&lt;/code&gt; — lowercase, exactly as spelled. This isn't just a convention; it's a hard dependency baked into the engine's internals. Many of the engine's built-in methods and utilities, such as &lt;code&gt;Component.crashWith()&lt;/code&gt;, &lt;code&gt;display.camera.follow()&lt;/code&gt;, and the various &lt;code&gt;move&lt;/code&gt; functions, directly reference a global variable named &lt;code&gt;display&lt;/code&gt;. For example, when you call &lt;code&gt;move.bound(player)&lt;/code&gt;, the function internally uses &lt;code&gt;display.canvas.width&lt;/code&gt; and &lt;code&gt;display.canvas.height&lt;/code&gt; to determine the screen boundaries. If you name your instance something else — say, &lt;code&gt;game&lt;/code&gt; or &lt;code&gt;app&lt;/code&gt; — these internal references will fail because &lt;code&gt;display&lt;/code&gt; will be undefined. You'll see cryptic errors like "Cannot read property 'canvas' of undefined" and your game will break. The engine was designed this way to keep the API simple for beginners, but it means you must follow this naming rule strictly.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use a Different Variable Name
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Wrong&lt;/span&gt;
&lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The engine has &lt;code&gt;display&lt;/code&gt; hardcoded in many places. When you use a different name, internal functions like &lt;code&gt;move.bound()&lt;/code&gt; and &lt;code&gt;Component.clicked()&lt;/code&gt; will look for a variable named &lt;code&gt;display&lt;/code&gt; and find nothing. This results in &lt;code&gt;undefined&lt;/code&gt; errors that can be confusing to debug. For example, &lt;code&gt;move.bound(player)&lt;/code&gt; checks &lt;code&gt;display.canvas.width&lt;/code&gt;, but if your instance is named &lt;code&gt;game&lt;/code&gt;, &lt;code&gt;display&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;, and you'll get "Cannot read property 'canvas' of undefined." The error messages won't point to your variable name — they'll point to the internal engine code, making it even harder to figure out. The only way to avoid this is to use the exact name &lt;code&gt;display&lt;/code&gt;. This is a small constraint that saves you from hours of debugging.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. ✅ DO: Call &lt;code&gt;display.perform()&lt;/code&gt; Before Using TileMaps
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;        &lt;span class="c1"&gt;// ✅ Creates fake canvas&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;        &lt;span class="c1"&gt;// ✅ TileMap works!&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileFace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Tiles appear!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The TileMap system relies on an offscreen canvas called &lt;code&gt;fake&lt;/code&gt; to render its tiles. This &lt;code&gt;fake&lt;/code&gt; canvas is only created when you call &lt;code&gt;display.perform()&lt;/code&gt;. Without &lt;code&gt;perform()&lt;/code&gt;, the &lt;code&gt;fake&lt;/code&gt; variable remains undefined, and the TileMap has nowhere to draw its tiles. The engine doesn't throw an error — it just silently fails to render anything, leaving you with a blank screen and no indication of what went wrong. This is especially frustrating because the code looks correct and no error messages appear. The &lt;code&gt;fake&lt;/code&gt; canvas is also where the engine caches static content for performance, so it's essential for more than just TileMaps. Always call &lt;code&gt;perform()&lt;/code&gt; before using any TileMap functionality. If you're using TileMaps, &lt;code&gt;perform()&lt;/code&gt; is mandatory, not optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use TileMap Without &lt;code&gt;display.perform()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ No perform() called&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;        &lt;span class="c1"&gt;// ❌ Creates tilemap&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileFace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Nothing appears!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Without &lt;code&gt;perform()&lt;/code&gt;, the &lt;code&gt;fake&lt;/code&gt; canvas doesn't exist. The TileMap has no offscreen buffer to render its tiles, so nothing is drawn. The engine doesn't throw any errors — it just silently fails. You'll see a blank screen and spend hours wondering why your tiles aren't appearing. This is one of the most common and frustrating mistakes beginners make because everything looks correct in the code. The solution is simple: always call &lt;code&gt;perform()&lt;/code&gt; before using any TileMap functionality.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. ✅ DO: Use &lt;code&gt;deltaTime&lt;/code&gt; for Movement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Frame-rate independent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;deltaTime&lt;/code&gt; parameter (often abbreviated as &lt;code&gt;dt&lt;/code&gt;) is the amount of time that has passed since the last frame, measured in seconds. On a 60Hz monitor, &lt;code&gt;dt&lt;/code&gt; is approximately 0.0167 seconds (1/60). On a 144Hz monitor, it's about 0.0069 seconds (1/144). When you multiply your speed values by &lt;code&gt;dt&lt;/code&gt;, you ensure that movement is frame-rate independent. For example, if you set &lt;code&gt;player.speedX = 200&lt;/code&gt;, and then in your update loop you write &lt;code&gt;player.x += player.speedX * dt&lt;/code&gt;, the player will move 200 pixels per second regardless of whether the game is running at 30fps, 60fps, or 144fps. On a 60Hz screen, each frame moves &lt;code&gt;200 * 0.0167 = 3.34&lt;/code&gt; pixels. On a 144Hz screen, each frame moves &lt;code&gt;200 * 0.0069 = 1.39&lt;/code&gt; pixels. Over the course of one second, both add up to exactly 200 pixels. This is essential for any game that should run consistently across different hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use Raw Numbers for Speed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Frame-rate dependent&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When you use raw numbers without &lt;code&gt;dt&lt;/code&gt;, your game's speed becomes tied to the frame rate. On a fast computer running at 144fps, a speed of 4 pixels per frame translates to 576 pixels per second. On a slow computer running at 30fps, the same speed of 4 pixels per frame translates to only 120 pixels per second. This means your game will run at completely different speeds on different computers — a player on a high-end machine will zoom through levels while a player on a low-end machine will crawl. This is a game-breaking issue that makes your game unplayable for a significant portion of your audience. Always multiply your speeds by &lt;code&gt;dt&lt;/code&gt; to ensure consistent gameplay across all devices.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. ✅ DO: Normalize Diagonal Movement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;0.707&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Prevents faster diagonal movement&lt;/span&gt;
    &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;0.707&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When you move a player or enemy by incrementing both &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; independently at the same speed, the diagonal speed becomes √(speed² + speed²) = speed × √2, which is approximately 1.414 times faster than horizontal or vertical movement. This happens because the two velocity vectors are added together at a right angle, and the resulting vector's magnitude is the square root of the sum of squares (the Pythagorean theorem). In practice, if your player moves at 200 pixels per second horizontally, moving diagonally will make them move at 282 pixels per second — a noticeable and disorienting speed boost. Players will feel like they're "sprinting" when moving diagonally, which breaks the consistency of your game's movement mechanics. To fix this, you need to normalize the direction vector. The most common approach is to multiply both &lt;code&gt;mx&lt;/code&gt; and &lt;code&gt;my&lt;/code&gt; by &lt;code&gt;1/√2 ≈ 0.707&lt;/code&gt; when both are non-zero, which reduces the combined speed back to exactly 1. This ensures that whether the movement is horizontal, vertical, or diagonal, the resulting speed is exactly the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Ignore Diagonal Normalization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When both left and up are pressed, the player moves at &lt;code&gt;√(200² + 200²) = 282&lt;/code&gt; pixels/second, which is faster than the intended 200 pixels/second. This is a fundamental principle of game physics and one of the most common "beginner traps" in game development.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6. ✅ DO: Use &lt;code&gt;display.add()&lt;/code&gt; for Every Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Required&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Creating a new &lt;code&gt;Component&lt;/code&gt; with &lt;code&gt;new Component(50, 50, "red", 100, 100)&lt;/code&gt; allocates memory for that object and sets up its properties — but it does &lt;strong&gt;not&lt;/strong&gt; automatically add it to the rendering pipeline. The engine maintains an internal list of components called &lt;code&gt;comm&lt;/code&gt; (short for "components") that it iterates over every frame. Inside &lt;code&gt;updat()&lt;/code&gt;, the engine loops through &lt;code&gt;comm&lt;/code&gt;, calls &lt;code&gt;move()&lt;/code&gt; on each component to update its position, and then calls &lt;code&gt;update()&lt;/code&gt; to draw it on the canvas. If you forget to call &lt;code&gt;display.add(player)&lt;/code&gt;, your player object exists in memory but is never added to &lt;code&gt;comm&lt;/code&gt;. As a result, &lt;code&gt;updat()&lt;/code&gt; never sees it, never updates its position, and never draws it. You'll have a perfectly valid &lt;code&gt;Component&lt;/code&gt; object that simply never appears on screen. This is a common source of frustration for beginners who think they've done everything right but see nothing. The rule is simple: every &lt;code&gt;Component&lt;/code&gt; you create must be passed to &lt;code&gt;display.add()&lt;/code&gt; to become part of the game world.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Forget to Add Components
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ❌ Missing display.add(player)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Without &lt;code&gt;display.add()&lt;/code&gt;, your component exists only in memory. It's like creating a character in a game but never spawning them into the world — they exist in the code but have no presence in the game. The engine's render loop iterates over the &lt;code&gt;comm&lt;/code&gt; array to draw components, but your component isn't in that array. No error is thrown; the component just never appears. This is one of the most common and frustrating bugs for beginners because everything looks correct in the code, but nothing shows up on screen. Always remember: creating a component is only half the job — you must also add it to the display.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. ✅ DO: Use &lt;code&gt;setImage()&lt;/code&gt; for Images
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Correct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;setImage()&lt;/code&gt; method was designed to handle the complexities of loading images in a browser. When you call &lt;code&gt;component.setImage("player.png")&lt;/code&gt;, the method creates a new &lt;code&gt;Image&lt;/code&gt; object, sets its &lt;code&gt;src&lt;/code&gt; property, and attaches &lt;code&gt;onload&lt;/code&gt; and &lt;code&gt;onerror&lt;/code&gt; event listeners. The &lt;code&gt;onload&lt;/code&gt; callback sets &lt;code&gt;imageLoaded = true&lt;/code&gt;, which the engine checks before attempting to draw the image. The &lt;code&gt;onerror&lt;/code&gt; callback provides a graceful fallback — if the image fails to load (due to a typo in the filename, network issues, or CORS restrictions), the component automatically converts itself to a red rectangle so your game doesn't crash with broken images. If you instead directly assign properties like &lt;code&gt;component.image = new Image(); component.image.src = "player.png";&lt;/code&gt;, you bypass all of this safety logic. The component's &lt;code&gt;type&lt;/code&gt; won't be set correctly, &lt;code&gt;imageLoaded&lt;/code&gt; will remain &lt;code&gt;false&lt;/code&gt;, and the engine might attempt to draw a non-existent image, causing runtime errors or invisible objects. Additionally, &lt;code&gt;setImage()&lt;/code&gt; handles the case where you might want to switch images dynamically — it properly cleans up the old image before loading the new one, preventing memory leaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Directly Assign Image Properties
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// ❌ Wrong&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;      &lt;span class="c1"&gt;// ❌ Wrong&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ❌ Wrong&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Images load asynchronously, which means the browser continues executing code while the image is being downloaded. If you directly assign the image and then immediately try to draw it, the image may not have finished loading yet. The engine's &lt;code&gt;imageLoaded&lt;/code&gt; flag is designed to handle this — it's set to &lt;code&gt;true&lt;/code&gt; only after the image has fully loaded. When you use &lt;code&gt;setImage()&lt;/code&gt;, this flag is properly managed. When you assign properties directly, you bypass this system, and the engine may try to draw an image that isn't ready yet, resulting in invisible objects or rendering errors. Additionally, &lt;code&gt;setImage()&lt;/code&gt; sets the component's &lt;code&gt;type&lt;/code&gt; to "image" automatically, which tells the engine how to render the component. If you forget to set the type, the engine will treat it as a rectangle and ignore the image entirely.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. ✅ DO: Use &lt;code&gt;destroy()&lt;/code&gt; for Permanent Removal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Removes forever&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;destroy()&lt;/code&gt; method is the proper way to permanently remove a component from your game. When you call &lt;code&gt;player.destroy()&lt;/code&gt;, the method searches the global &lt;code&gt;comm&lt;/code&gt; array (and the &lt;code&gt;commp&lt;/code&gt; array used by the fake canvas) for references to that component and removes them. It then sets the component's &lt;code&gt;update&lt;/code&gt; method to &lt;code&gt;null&lt;/code&gt;, effectively cutting all ties to the rendering pipeline. This allows the JavaScript garbage collector to reclaim the memory used by the component and its associated resources (including images, which can be large). If you instead use &lt;code&gt;hide()&lt;/code&gt; to make a component invisible, the component is still present in the &lt;code&gt;comm&lt;/code&gt; array. Every frame, the engine will still call &lt;code&gt;move()&lt;/code&gt; on it (updating its position), check its collision with other objects, and attempt to call &lt;code&gt;update()&lt;/code&gt; on it (which now does nothing because you set &lt;code&gt;update = null&lt;/code&gt;). This wastes CPU cycles — especially problematic if you have hundreds of destroyed enemies still being processed. &lt;code&gt;hide()&lt;/code&gt; is designed for temporary invisibility, like when a coin is collected and you want to respawn it later. For permanent removal, always use &lt;code&gt;destroy()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Hide Objects You'll Never Use Again
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Still in memory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When you hide an object, it remains in the engine's internal arrays and continues to be processed every frame. The engine still calls &lt;code&gt;move()&lt;/code&gt; on it, updating its position based on speed and physics. It still checks collision with other objects. The only thing that stops is drawing. This means you're wasting CPU cycles on objects that will never be seen again. If you have 100 bullets hidden this way, you're still updating 100 bullets every frame, which slows down your game. Worse, because the objects are still in memory, they contribute to memory usage and can cause performance issues over time. Use &lt;code&gt;hide()&lt;/code&gt; only for objects that you plan to show again later. For objects that are gone forever, use &lt;code&gt;destroy()&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. ✅ DO: Use &lt;code&gt;hide()&lt;/code&gt; for Temporary Invisibility
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Disappears but can be shown later&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;hide()&lt;/code&gt; method is designed for scenarios where you need a component to disappear temporarily but plan to bring it back later. When you call &lt;code&gt;coin.hide()&lt;/code&gt;, the method sets the component's &lt;code&gt;update&lt;/code&gt; method to &lt;code&gt;null&lt;/code&gt;, which prevents it from being drawn on screen. However, the component remains in the &lt;code&gt;comm&lt;/code&gt; array and continues to be part of the game loop — its position is still updated, its collision is still checked, and it still exists in memory. This is perfect for a coin that the player just collected but that you want to respawn after 10 seconds. When it's time to respawn, you simply call &lt;code&gt;coin.show()&lt;/code&gt;, which restores the &lt;code&gt;update&lt;/code&gt; method to its original drawing function, and the coin reappears on screen. If you used &lt;code&gt;destroy()&lt;/code&gt; instead, the coin would be completely removed from memory and you'd have to create a brand new component from scratch, with all the overhead of allocating memory, loading images, and re-adding it to the display. This becomes especially important for high-frequency objects like bullets or particles — creating and destroying thousands of objects per second would trigger frequent garbage collection, causing visible stutter.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use &lt;code&gt;destroy()&lt;/code&gt; for Temporary Removal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Gone forever&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;destroy()&lt;/code&gt; is permanent — it removes the component from the engine's arrays, sets its methods to &lt;code&gt;null&lt;/code&gt;, and marks it for garbage collection. If you call &lt;code&gt;destroy()&lt;/code&gt; on an enemy that you planned to respawn later, you'll have to create a whole new enemy from scratch. This means allocating new memory, setting up all the properties again, loading any images again, and adding it back to the display. This is inefficient and can cause performance issues if done frequently. Use &lt;code&gt;destroy()&lt;/code&gt; only when you're absolutely sure you won't need the object again. For objects that you'll reuse (like enemies in a wave-based game), use &lt;code&gt;hide()&lt;/code&gt; to make them invisible and then &lt;code&gt;show()&lt;/code&gt; to bring them back.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. ✅ DO: Use &lt;code&gt;Tctxt&lt;/code&gt; for UI Text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;Tctxt&lt;/code&gt; class is a specialized text component that provides features regular text doesn't have. Unlike a basic &lt;code&gt;Component&lt;/code&gt; with &lt;code&gt;type = "text"&lt;/code&gt;, &lt;code&gt;Tctxt&lt;/code&gt; supports background colors with padding (so your text has a nice box behind it), text alignment (left, center, right), text stroke (outline effect), and baseline control. It also automatically measures text width to handle alignment correctly. When you use &lt;code&gt;Tctxt&lt;/code&gt;, your UI will look polished and professional. If you use a basic &lt;code&gt;Component&lt;/code&gt; for text, your text will be drawn directly on the canvas with no background, no padding, and no alignment options. This makes your UI look plain and hard to read, especially over complex backgrounds. For any UI element that displays text — scores, health bars, menus, dialog boxes — always use &lt;code&gt;Tctxt&lt;/code&gt; for a cleaner, more professional look.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use &lt;code&gt;Component&lt;/code&gt; for Text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The basic &lt;code&gt;Component&lt;/code&gt; class treats text as a simple rendering operation — it draws the text at the specified position with the specified font and color, and that's it. There's no background, no padding, no alignment, and no stroke. This means your text will be drawn directly on top of whatever is behind it, making it hard to read if the background is busy or has similar colors. You also can't center the text easily because you'd have to manually calculate the text width and adjust the position. &lt;code&gt;Tctxt&lt;/code&gt; handles all of this for you, providing a polished UI with minimal effort. For any game that needs to display information to the player, &lt;code&gt;Tctxt&lt;/code&gt; is the way to go.&lt;/p&gt;




&lt;h3&gt;
  
  
  11. ✅ DO: Call &lt;code&gt;.fixed()&lt;/code&gt; on UI Elements Every Frame
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Keeps UI on screen&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;.fixed()&lt;/code&gt; method adjusts a component's position based on the camera's current position. When you call &lt;code&gt;.fixed()&lt;/code&gt;, the component's &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are recalculated to be relative to the camera's viewport. This ensures that the UI element stays in the same place on the screen, regardless of where the camera moves. Without &lt;code&gt;.fixed()&lt;/code&gt;, UI elements would scroll with the camera — they'd move off-screen when the camera moves, and appear to drift around the world. This is because components are positioned in world space by default. By calling &lt;code&gt;.fixed()&lt;/code&gt; every frame, you're telling the engine "this element should be fixed to the screen, not the world." You need to call it every frame because the camera's position can change every frame. If you call it once, the UI will be fixed to the camera's position at that moment, but when the camera moves, the UI will stay in the wrong place. For any UI element that should stay on screen (score, health, menus), always call &lt;code&gt;.fixed()&lt;/code&gt; in your &lt;code&gt;update()&lt;/code&gt; loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Call &lt;code&gt;.fixed()&lt;/code&gt; Once
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Called only once&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;.fixed()&lt;/code&gt; works by setting the component's position relative to the camera's current position. If you call it once at the start of the game, the component is positioned correctly for that moment. But as soon as the camera moves — because the player walks to the right, for example — the component's position is no longer correct. It stays in the world position it was fixed to, which is now off-screen or in the wrong place. To keep UI elements correctly positioned, you must call &lt;code&gt;.fixed()&lt;/code&gt; every frame, so that the position is recalculated based on the camera's current position. This ensures that your UI stays in the same place on the screen, no matter where the camera moves.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏃 Movement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  12. ✅ DO: Use &lt;code&gt;move.bound()&lt;/code&gt; to Keep Objects On Screen
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Keeps player on screen&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;move.bound()&lt;/code&gt; is a single-line solution that replaces four separate &lt;code&gt;if&lt;/code&gt; statements. When you call &lt;code&gt;move.bound(player)&lt;/code&gt;, the function checks if the player's &lt;code&gt;x&lt;/code&gt; is less than 0 and sets it to 0 if so; checks if &lt;code&gt;x + width&lt;/code&gt; exceeds &lt;code&gt;display.canvas.width&lt;/code&gt; and sets it to &lt;code&gt;canvas.width - width&lt;/code&gt;; and does the same for &lt;code&gt;y&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;. This keeps your player (or any component) within the visible screen area. Writing this yourself isn't difficult — it's just four conditionals — but doing it manually for every component that needs boundary checking is repetitive and error-prone. You might forget to subtract the component's width when checking the right edge, or accidentally use &lt;code&gt;height&lt;/code&gt; instead of &lt;code&gt;width&lt;/code&gt;. You might also need to check boundaries for enemies, bullets, and UI elements, leading to duplicated code. &lt;code&gt;move.bound()&lt;/code&gt; centralizes this logic in one well-tested location, reducing bugs and keeping your code clean. It also has a companion function, &lt;code&gt;move.boundTo()&lt;/code&gt;, which allows you to specify custom boundaries (like keeping a boss enemy within a specific arena), giving you even more flexibility without writing custom logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Write Your Own Edge Detection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Writing your own edge detection is repetitive and error-prone. You have to remember to check all four edges, and you have to remember to subtract the component's width and height when checking the right and bottom edges. If you forget, your object will partially go off-screen before being stopped. You also have to do this for every component that needs boundary checking, leading to duplicated code. &lt;code&gt;move.bound()&lt;/code&gt; handles all of this for you in a single function call, with all the edge cases handled correctly. It's faster to write, faster to read, and less likely to contain bugs.&lt;/p&gt;




&lt;h3&gt;
  
  
  13. ✅ DO: Use &lt;code&gt;move.accelerate()&lt;/code&gt; and &lt;code&gt;move.decelerate()&lt;/code&gt; for Smooth Physics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Smooth acceleration&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// ✅ Smooth deceleration&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;move.accelerate()&lt;/code&gt; and &lt;code&gt;move.decelerate()&lt;/code&gt; provide smooth, weighty movement that feels realistic and responsive. &lt;code&gt;move.accelerate(player, 0.5, 0.5, 5, 5)&lt;/code&gt; adds 0.5 pixels per second² to the player's &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt;, while capping the maximum speed at 5 pixels per second in each direction. This creates a feeling of momentum — the player gradually speeds up to their maximum velocity rather than instantly teleporting to max speed. &lt;code&gt;move.decelerate()&lt;/code&gt; does the opposite, reducing speed toward zero, which creates friction and a sense of weight. If you directly set &lt;code&gt;player.speedX = 5&lt;/code&gt; and &lt;code&gt;player.speedY = 5&lt;/code&gt;, the player immediately moves at full speed with no acceleration or deceleration, resulting in twitchy, unnatural movement that feels like ice-skating. This is especially noticeable in platformers, where acceleration and deceleration give the player character a sense of weight and control. Directly setting speed is appropriate for some scenarios (like teleportation or menu navigation), but for character movement in games, acceleration-based physics is almost always preferable.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Directly Set Speed for Physics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Instant speed change&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Instant stop&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Directly setting speed creates jerky, unnatural movement. When you press a key, the player instantly jumps to full speed; when you release the key, the player instantly stops. This feels like ice-skating or sliding on ice — there's no sense of weight or momentum. In most games, players expect a gradual acceleration and deceleration. This makes the movement feel more responsive and gives the player a sense of control. &lt;code&gt;move.accelerate()&lt;/code&gt; and &lt;code&gt;move.decelerate()&lt;/code&gt; provide this smooth, weighty feel with just a few lines of code.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Rendering
&lt;/h2&gt;

&lt;h3&gt;
  
  
  14. ✅ DO: Use the Fake Canvas for Static Content
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// ✅ Cached, drawn once&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The fake canvas is an offscreen buffer that renders static content once and then caches it as an image. This is the core of the engine's performance optimization. When you add a component to the fake canvas (using &lt;code&gt;fake.add()&lt;/code&gt;), it's drawn onto the fake canvas's context. The fake canvas is then drawn as a single image onto the main canvas every frame. This means that instead of drawing 100 trees individually every frame (each requiring its own draw call), the engine draws one image — the fake canvas — in a single draw call. This drastically reduces the number of draw calls, which is the primary bottleneck in 2D rendering. The difference can be dramatic: a game with 1000 static objects might run at 4fps without the fake canvas, but at 60fps with it. The fake canvas is ideal for backgrounds, tilemaps, decorations, and any other content that doesn't move. For dynamic content like players, enemies, and particles, use the main canvas with &lt;code&gt;display.add()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Put Static Content on the Main Canvas
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// ❌ Redrawn every frame&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Every object on the main canvas is redrawn every frame. If you have 100 trees, the engine makes 100 draw calls every frame. Each draw call has overhead — the engine has to set up the context, apply transformations, and draw the object. This overhead adds up quickly. With 100 objects, you might not notice; with 1000 objects, your game will slow down significantly. The fake canvas solves this by combining all static objects into a single image. Instead of 100 draw calls, you have 1. This is one of the most important optimizations in the engine, and it's why the fake canvas exists. Always use it for static content.&lt;/p&gt;




&lt;h3&gt;
  
  
  15. ✅ DO: Call &lt;code&gt;fake.refresh()&lt;/code&gt; After Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bgComm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;new_sky.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Forces redraw&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The fake canvas caches its content to maximize performance. When you change the content of the fake canvas — for example, by changing the image of a background component, adding or removing tiles, or modifying any static object — the cached image becomes outdated. The engine won't automatically know that the content has changed, so it will continue to display the old cached image. To update the cached image, you need to call &lt;code&gt;fake.refresh()&lt;/code&gt;. This forces the engine to clear the fake canvas, redraw all the static components, and cache the new image. If you forget to call &lt;code&gt;fake.refresh()&lt;/code&gt;, your changes won't appear on screen, and you'll be stuck wondering why your new sky or updated tilemap isn't showing. Always call &lt;code&gt;fake.refresh()&lt;/code&gt; after making any changes to the fake canvas.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Forget to Refresh
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bgComm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;new_sky.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ❌ Missing fake.refresh()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The fake canvas is designed to be static — it renders once and then reuses the same image every frame. This is what makes it so performant. However, when you change the content of the fake canvas, the cached image becomes outdated. The engine doesn't automatically detect this change, so it continues to use the old cached image. Your changes appear to have no effect, even though the code is correct. This is a common source of confusion for beginners. Always remember to call &lt;code&gt;fake.refresh()&lt;/code&gt; after modifying any content on the fake canvas.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎥 Camera
&lt;/h2&gt;

&lt;h3&gt;
  
  
  16. ✅ DO: Set &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The camera needs to know the boundaries of your game world so it can clamp its position and never show empty space outside the world. When the camera follows the player, it calculates its position based on the player's location. However, if the player moves to the edge of the world, the camera might try to move beyond the world boundaries, showing black space or empty areas. By setting &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt;, you tell the camera the maximum extent of your world. The camera's &lt;code&gt;follow()&lt;/code&gt; method uses these values to clamp the camera's position so that it never shows areas outside the world. Without these values, the camera defaults to 1000x1000, which may not match your world size. If your world is larger (say, 2000x2000), the camera will stop at 1000 pixels, cutting off half your world. If your world is smaller (say, 800x600), the camera will show empty space beyond the world boundaries. Always set &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt; to match your world's actual size.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Leave World Bounds Unset
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Missing worldWidth and worldHeight&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Without world bounds, the camera has no idea where the world ends. When the player reaches the edge of the world, the camera will continue moving beyond the world boundary, showing empty space. This breaks the illusion of a cohesive game world and can be disorienting for players. The default bounds of 1000x1000 are arbitrary and won't match most games. Always set &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt; to match your level's dimensions.&lt;/p&gt;




&lt;h3&gt;
  
  
  17. ✅ DO: Use Camera Shake for Impact
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Impact feedback&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Camera shake is a powerful visual feedback mechanism that makes impacts feel more intense and satisfying. When the player collides with an enemy, takes damage, or triggers an explosion, a brief camera shake communicates the force of the event to the player. The shake is created by temporarily displacing the camera's position (and optionally its rotation) and then quickly returning it to normal. The &lt;code&gt;camera.shake(x, y)&lt;/code&gt; method applies a displacement of &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; pixels, and then automatically reverts after a short duration (approximately 1/24 of a second). This creates a brief, sharp jolt that tells the player "something significant just happened." Without camera shake, impacts can feel flat and unsatisfying. However, camera shake should be used sparingly — too much shake is disorienting and can make players dizzy. A good rule of thumb is to use shake for major events (player hit, boss death, explosion) but not for minor events (collecting a coin, jumping).&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Overuse Camera Shake
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Too intense&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Camera shake is like spice — a little bit enhances the flavor, but too much ruins the dish. When you use camera shake too frequently or with too much intensity, it becomes disorienting and annoying. Players may feel dizzy or nauseous, especially if the shake is combined with rapid movement. Use camera shake sparingly, for major events only, and keep the intensity moderate. A shake of 5-10 pixels is usually sufficient to convey impact without being overwhelming.&lt;/p&gt;




&lt;h3&gt;
  
  
  18. ✅ DO: Use &lt;code&gt;camera.follow()&lt;/code&gt; Every Frame
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Called every frame&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The camera's &lt;code&gt;follow()&lt;/code&gt; method calculates the camera's position based on the target's position. However, &lt;code&gt;follow()&lt;/code&gt; only updates the camera's position at the moment it's called. If you call &lt;code&gt;follow()&lt;/code&gt; once at the start of the game, the camera will lock onto the player's position at that exact moment and never update again. When the player moves, the camera will stay in its original position, and the player will eventually move off-screen. To keep the camera following the player smoothly, you need to call &lt;code&gt;follow()&lt;/code&gt; every frame, typically in your &lt;code&gt;update()&lt;/code&gt; loop. This ensures that the camera's position is recalculated based on the player's current position every frame, keeping the player centered on the screen (or at the specified offset). The second parameter, &lt;code&gt;smooth&lt;/code&gt;, enables smooth interpolation, which makes the camera movement less jerky by easing the camera toward the target position over several frames. For most games, smooth following is preferable for a polished feel.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Call &lt;code&gt;camera.follow()&lt;/code&gt; Once
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Called only once&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;camera.follow()&lt;/code&gt; is not a one-time setup — it's a continuous operation that needs to be performed every frame. If you only call it once, the camera will set its position to follow the player at that exact moment and then never update again. As the player moves, the camera stays in its original position, and the player eventually moves off-screen. This completely defeats the purpose of a following camera. Always call &lt;code&gt;camera.follow()&lt;/code&gt; in your &lt;code&gt;update()&lt;/code&gt; loop to keep the camera tracking the player.&lt;/p&gt;




&lt;h3&gt;
  
  
  19. ✅ DO: Set Zoom When It Changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setZoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Only when zoom changes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;camera.setZoom()&lt;/code&gt; method applies a scale transformation to the rendering context, which affects how everything is drawn. This transformation is applied every frame as part of the rendering process. However, if you call &lt;code&gt;setZoom()&lt;/code&gt; with the same value every frame, you're forcing the engine to recalculate and apply the same transformation 60 times per second, which wastes CPU cycles. The zoom level typically changes only when the player uses a zoom-in or zoom-out control, or during specific game events (like a sniper scope). For these cases, you should call &lt;code&gt;setZoom()&lt;/code&gt; only when the zoom value actually changes. Store the current zoom level in a variable, and only call &lt;code&gt;setZoom()&lt;/code&gt; when the variable changes. This reduces unnecessary calculations and improves performance, especially on lower-end devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Set Zoom Every Frame
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setZoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Every frame&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Setting zoom every frame is a waste of CPU cycles. The zoom level is a transformation that's applied to the canvas context. When you call &lt;code&gt;setZoom()&lt;/code&gt;, the engine has to recalculate the transformation matrix and apply it to the context. This is a small operation, but when done 60 times per second unnecessarily, it adds up. More importantly, it clutters your code and makes it harder to read. Only set the zoom when it actually changes — typically when the player presses a zoom key or when a game event triggers a zoom change.&lt;/p&gt;




&lt;h2&gt;
  
  
  🗺️ TileMaps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  20. ✅ DO: Set Fake Canvas Size to World Size
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The fake canvas serves as the offscreen buffer for your tilemap. When you render a tilemap, the tiles are drawn onto the fake canvas, and the fake canvas is then drawn as a single image on the main canvas. For this to work correctly, the fake canvas needs to be large enough to hold the entire tilemap. If the fake canvas is smaller than the tilemap, the tiles that fall outside the fake canvas boundaries won't be drawn. If the fake canvas is larger than needed, it wastes memory and might cause performance issues. The ideal size for the fake canvas is exactly the size of the tilemap in pixels, which should match the &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt; you set for the camera. By setting &lt;code&gt;fake.canvas.width&lt;/code&gt; and &lt;code&gt;fake.canvas.height&lt;/code&gt; to the world size, you ensure that the entire tilemap fits perfectly on the fake canvas, with no wasted space or missing tiles.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Sync Camera Instead
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Defeats caching&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The fake canvas is designed to be a static cache. If you start moving the fake camera, you defeat the entire purpose of caching. The fake canvas is meant to be rendered once and then reused as a single image. If you move the fake camera, you're effectively trying to render the tilemap from a different perspective each frame, which means you'd have to redraw the tilemap every frame — exactly what the fake canvas was designed to avoid. The correct approach is to render the entire tilemap once on the fake canvas (with the fake camera at the origin), and then use the main camera to view different parts of the fake canvas.&lt;/p&gt;




&lt;h3&gt;
  
  
  21. ✅ DO: Use &lt;code&gt;tilemap.crashWith()&lt;/code&gt; for Collision
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tilemap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Collision with walls&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle collision&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;tilemap.crashWith()&lt;/code&gt; method is an efficient way to check collision between a component and any tiles in the tilemap. You can optionally specify a tile ID to check collision only with specific tile types (e.g., ID 1 for walls, ID 2 for water, ID 3 for spikes). The method loops through the tilemap's internal list of tiles and checks collision between the component and each tile of the specified type. This is much more efficient than manually looping through all tiles and checking collision yourself, because &lt;code&gt;crashWith()&lt;/code&gt; uses the engine's internal optimized collision detection, and it only checks tiles that are relevant (based on the tile ID filter). If you manually check collision with a &lt;code&gt;for&lt;/code&gt; loop, you'll be duplicating logic that the engine already provides, and you'll likely miss the optimization that &lt;code&gt;crashWith()&lt;/code&gt; implements. For any tile-based collision, always use &lt;code&gt;tilemap.crashWith()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use Manual Tile Collision
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tilemap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle collision&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Manual tile collision is inefficient and error-prone. You have to manually retrieve the tiles, loop through them, and check collision with each one. The &lt;code&gt;tilemap.crashWith()&lt;/code&gt; method does all of this for you, with optimized internal logic that's faster and more reliable. Additionally, &lt;code&gt;crashWith()&lt;/code&gt; only checks tiles that are near the component, not all tiles in the map, making it much more performant for large maps. Always use &lt;code&gt;tilemap.crashWith()&lt;/code&gt; instead of writing your own collision loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Performance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  22. ✅ DO: Use Object Pooling for High-Frequency Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BulletPool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pre-creates bullets and reuses them&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Object pooling is a design pattern where you pre-create a set of objects and reuse them instead of creating and destroying them on the fly. In JavaScript, creating a new object with &lt;code&gt;new Component()&lt;/code&gt; allocates memory, and destroying it with &lt;code&gt;destroy()&lt;/code&gt; eventually triggers garbage collection when the memory is reclaimed. Garbage collection pauses the JavaScript engine, causing frame drops and stutter. If you're creating and destroying hundreds of objects per second (like bullets, particles, or enemies), garbage collection will happen frequently, causing visible lag. Object pooling avoids this by creating a fixed number of objects upfront and reusing them. When you need a bullet, you take one from the pool. When the bullet is done, you return it to the pool (by hiding it and resetting its properties). This eliminates constant allocation and garbage collection, resulting in smoother performance. For any game with high-frequency object creation and destruction, object pooling is essential.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Create and Destroy Objects Constantly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bullet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Creates new object every time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Creating and destroying objects constantly is a performance killer. Each &lt;code&gt;new&lt;/code&gt; allocates memory, and each &lt;code&gt;destroy()&lt;/code&gt; eventually triggers garbage collection. Garbage collection pauses the JavaScript engine to clean up unused memory, causing frame drops and stutter. In a game where the player shoots 10 bullets per second, you're creating 10 new objects per second and destroying them shortly after. This will cause frequent garbage collection, making your game feel laggy and unresponsive. Object pooling is a simple and effective solution to this problem.&lt;/p&gt;




&lt;h3&gt;
  
  
  23. ✅ DO: Remove Objects from Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enemies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Removes from array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; When you use &lt;code&gt;splice()&lt;/code&gt; to remove an element from an array, the array shrinks, reducing the number of iterations in future loops. This is especially important if you're iterating over the array frequently, such as in the &lt;code&gt;update()&lt;/code&gt; loop where you process enemy AI, movement, and collision. A smaller array means fewer loop iterations, which means less CPU usage and better performance. Additionally, &lt;code&gt;splice()&lt;/code&gt; properly removes the object from the array, allowing the garbage collector to reclaim its memory. If you instead set the element to &lt;code&gt;null&lt;/code&gt;, the array remains the same size, and you'll need to check for &lt;code&gt;null&lt;/code&gt; values in your loops, which adds unnecessary branching and CPU overhead. Over time, a large array with many &lt;code&gt;null&lt;/code&gt; values will slow down your game. Always use &lt;code&gt;splice()&lt;/code&gt; (or &lt;code&gt;filter()&lt;/code&gt;) to remove objects from arrays.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Set Objects to &lt;code&gt;null&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enemies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Still in array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Setting an element to &lt;code&gt;null&lt;/code&gt; doesn't remove it from the array — it just leaves an empty slot. The array length stays the same, which means your loops will still iterate over that slot. You'll have to add a check for &lt;code&gt;null&lt;/code&gt; in your loops, which adds an extra branch and wastes CPU cycles. Over time, as more and more objects are set to &lt;code&gt;null&lt;/code&gt;, your arrays will grow large with many empty slots, and your loops will waste time checking for &lt;code&gt;null&lt;/code&gt; values. Use &lt;code&gt;splice()&lt;/code&gt; to actually remove elements from the array.&lt;/p&gt;




&lt;h3&gt;
  
  
  24. ✅ DO: Use &lt;code&gt;clearMargin&lt;/code&gt; Optimization
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearMargin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Optimized&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;clearMargin&lt;/code&gt; array controls the size of the area that the engine clears on the canvas before rendering. By default, it's set to &lt;code&gt;[width*width, height*height]&lt;/code&gt;, which is the square of the canvas dimensions. For a canvas sized 800x600, this defaults to &lt;code&gt;[640000, 360000]&lt;/code&gt; — a massive area far larger than the visible canvas. This means the engine is clearing a huge rectangle off-screen, which wastes CPU cycles and can hurt performance. By setting &lt;code&gt;clearMargin&lt;/code&gt; to the actual canvas dimensions (e.g., &lt;code&gt;[800, 600]&lt;/code&gt;), you reduce the cleared area to exactly what's visible on screen, saving CPU cycles and improving performance. This optimization is especially noticeable on lower-end devices. Always set &lt;code&gt;clearMargin&lt;/code&gt; to match your canvas size after calling &lt;code&gt;display.start()&lt;/code&gt; or &lt;code&gt;display.scale()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Keep the Default
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// display.clearMargin = [640000, 360000];  // ❌ Huge area&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The default &lt;code&gt;clearMargin&lt;/code&gt; is &lt;code&gt;[width*width, height*height]&lt;/code&gt;, which for a 800x600 canvas is &lt;code&gt;[640000, 360000]&lt;/code&gt;. This is far larger than the visible canvas, meaning the engine is clearing a huge area off-screen that will never be drawn. This wastes CPU cycles and can cause performance issues, especially on lower-end devices. Always set &lt;code&gt;clearMargin&lt;/code&gt; to the actual canvas dimensions to minimize the clearing area.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎵 Audio
&lt;/h2&gt;

&lt;h3&gt;
  
  
  25. ✅ DO: Preload Sounds Before &lt;code&gt;display.start()&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jump.wav&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Preloaded&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Sound files need to be loaded from the server before they can be played. The &lt;code&gt;Sound&lt;/code&gt; class loads the audio file asynchronously in the background. If you create a &lt;code&gt;Sound&lt;/code&gt; object and immediately call &lt;code&gt;play()&lt;/code&gt;, the audio file may not be fully loaded yet, causing a delay or a silent failure. Worse, if you create a &lt;code&gt;Sound&lt;/code&gt; object during gameplay (e.g., in the &lt;code&gt;update()&lt;/code&gt; loop), you're forcing the engine to load an audio file while the game is running, which causes lag and stutter. To avoid this, you should preload all sounds before the game starts. Create your &lt;code&gt;Sound&lt;/code&gt; objects at the beginning of the game (before calling &lt;code&gt;display.start()&lt;/code&gt;), and store them in variables or in a &lt;code&gt;SoundManager&lt;/code&gt;. This gives the audio files time to load while the game is initializing, ensuring that they're ready to play when you need them. Preloading is especially important for sounds that play frequently, like jump sounds or collision sounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Play Sounds Without Preloading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jump.wav&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Created during gameplay&lt;/span&gt;
    &lt;span class="nx"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Creating a &lt;code&gt;Sound&lt;/code&gt; object during gameplay forces the engine to load the audio file while the game is running. This causes a delay while the file is downloaded and decoded, resulting in lag and stutter. This is especially problematic for sounds that play frequently, like jump sounds or collision sounds, because the lag would happen every time the sound plays. Preloading all sounds at the start of the game eliminates this issue, ensuring that sounds play instantly when triggered.&lt;/p&gt;




&lt;h3&gt;
  
  
  26. ✅ DO: Use &lt;code&gt;move.sound.*&lt;/code&gt; for Quick Audio
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sound&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jump&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Shortcut&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;move.sound&lt;/code&gt; object provides a convenient shortcut for playing sounds that have been loaded into the global &lt;code&gt;soundManager&lt;/code&gt;. Instead of writing &lt;code&gt;if (soundManager) { soundManager.play("jump"); }&lt;/code&gt;, you can simply write &lt;code&gt;move.sound.play("jump")&lt;/code&gt;. The &lt;code&gt;move.sound&lt;/code&gt; wrapper handles null references (if &lt;code&gt;soundManager&lt;/code&gt; doesn't exist) and manages volume automatically. It also provides shortcuts for music control (&lt;code&gt;move.sound.playMusic()&lt;/code&gt;, &lt;code&gt;move.sound.stopMusic()&lt;/code&gt;) and global volume control (&lt;code&gt;move.sound.setMasterVolume()&lt;/code&gt;, &lt;code&gt;move.sound.mute()&lt;/code&gt;). Using &lt;code&gt;move.sound&lt;/code&gt; reduces code verbosity and makes your audio code cleaner and easier to read. For simple audio playback, always prefer &lt;code&gt;move.sound&lt;/code&gt; over manually calling &lt;code&gt;soundManager&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Manually Handle Audio for Simple Cases
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;soundManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;soundManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jump&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ More code&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Manually checking for &lt;code&gt;soundManager&lt;/code&gt; and calling &lt;code&gt;play()&lt;/code&gt; is verbose and repetitive. The &lt;code&gt;move.sound&lt;/code&gt; wrapper handles these checks for you, with less code and less room for error. Additionally, &lt;code&gt;move.sound&lt;/code&gt; provides volume management and other features that you'd have to implement yourself if you manually handled audio. Use &lt;code&gt;move.sound&lt;/code&gt; for cleaner, more reliable audio code.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Testing &amp;amp; Debugging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  27. ✅ DO: Use &lt;code&gt;console.log()&lt;/code&gt; for Debugging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Player position:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; The &lt;code&gt;console.log()&lt;/code&gt; function is a simple and effective way to understand what's happening in your game. By logging variables, positions, and states, you can trace the flow of your game and identify where things are going wrong. For example, if your player isn't moving, you can log the &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt; values to see if they're being set correctly. If a collision isn't working, you can log the positions of the colliding objects to see if they're overlapping. Logging is especially useful for reproducing and diagnosing bugs that are hard to see visually. However, it's important to remove or comment out debug logs in production builds, as logging every frame can slow down the game and clutter the console with unnecessary information. Use logging liberally during development, but clean it up before releasing your game.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Leave Debug Logs in Production
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frame:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frameNo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ Debug in production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Logging every frame is a performance drain. Each &lt;code&gt;console.log()&lt;/code&gt; call takes time, and if you're logging 60 times per second, it can add up and slow down your game. Additionally, leaving debug logs in production clutters the console with unnecessary information, making it harder to see important messages. Always remove debug logs before releasing your game.&lt;/p&gt;




&lt;h3&gt;
  
  
  28. ✅ DO: Test on Different Browsers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Test on Chrome, Firefox, Edge, Safari&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Different browsers implement JavaScript, Canvas, and audio differently. What works perfectly in Chrome might break in Safari, or what's fast in Firefox might be slow in Edge. This is especially true for advanced features like WebGL, audio contexts, and touch events. To ensure your game works for all users, you should test on multiple browsers, including different versions. At minimum, test on Chrome (the most common browser), Firefox (for its excellent developer tools), and Safari (for iOS users). If possible, also test on Edge and on mobile browsers (Safari on iOS, Chrome on Android). Testing on different browsers will help you catch compatibility issues early and ensure that your game provides a consistent experience for all players.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Only Test on One Browser
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Only test on Chrome&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Testing only on Chrome is a common mistake. While Chrome is the most popular browser, it's not the only one. Safari, Firefox, and Edge all have significant market shares. Features that work in Chrome may not work in other browsers, or may work differently. By only testing on Chrome, you risk releasing a game that's broken for a significant portion of your audience. Always test on multiple browsers to ensure compatibility.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 Documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  29. ✅ DO: Comment Your Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── PLAYER MOVEMENT ──&lt;/span&gt;
&lt;span class="c1"&gt;// Arrow keys move the player at 200px/s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Comments are essential for understanding your own code, especially when you come back to it after a few weeks or months. A well-commented codebase is easier to debug, easier to extend, and easier to share with others. Comments should explain the "why" behind the code — the intent, the reasoning, and the trade-offs. For example, instead of just writing &lt;code&gt;if (display.keys[37]) player.speedX = -200 * dt;&lt;/code&gt;, you might comment &lt;code&gt;// Left arrow: move player left at 200px/s, using dt for frame-rate independence&lt;/code&gt;. This helps you (and others) quickly understand what the code does without having to decipher it. Comments are especially useful for complex logic, math, and game mechanics. Always comment your code, especially the parts that aren't immediately obvious.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Leave Code Uncommented
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Uncommented code is hard to understand. When you come back to your code after a few weeks, you might not remember why you wrote a particular line or what it's supposed to do. This makes debugging and extending your code much more difficult. Comments help you and others understand the code more quickly and with less effort.&lt;/p&gt;




&lt;h3&gt;
  
  
  30. ✅ DO: Use Meaningful Variable Names
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ✅ Clear&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Meaningful variable names make your code self-documenting. When you read &lt;code&gt;const playerSpeed = 200;&lt;/code&gt;, you immediately know that this variable represents the player's movement speed. When you read &lt;code&gt;const a = 200;&lt;/code&gt;, you have no idea what &lt;code&gt;a&lt;/code&gt; represents. This becomes a problem when you have dozens of variables — you'll constantly have to remember what each one does, or look back at where it was defined. Meaningful names reduce cognitive load, make code easier to read and maintain, and reduce the number of bugs caused by misinterpretation. In general, variable names should be descriptive but not overly long. &lt;code&gt;playerSpeed&lt;/code&gt; is a good name; &lt;code&gt;playerMovementSpeedInPixelsPerSecond&lt;/code&gt; is too long. Avoid abbreviations that aren't immediately obvious (&lt;code&gt;pSpeed&lt;/code&gt; vs &lt;code&gt;playerSpeed&lt;/code&gt;). Good naming is a habit that pays off in every line of code you write.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Use Cryptic Names
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ What is 'a'?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Cryptic variable names make code hard to read and understand. When you use names like &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, or &lt;code&gt;temp&lt;/code&gt;, you're forcing yourself and others to remember what each variable represents. This is especially problematic in longer functions where you might have many variables. Meaningful names make your code self-documenting and reduce the need for comments. Always use descriptive names that clearly indicate what the variable represents.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Final Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  31. ✅ DO: Have Fun and Experiment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Try new things, break things, learn from mistakes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Game development is a creative and iterative process. The best way to learn is to experiment, try new things, and make mistakes. Don't be afraid to break your game — that's how you learn what works and what doesn't. Every mistake teaches you something valuable, whether it's a subtle bug that took hours to find or a design choice that didn't work out. The most successful game developers are the ones who aren't afraid to fail and learn from their failures. So have fun with it! Try weird ideas, push the engine to its limits, and see what you can create. The journey of learning and building is just as rewarding as the final product.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ DON'T: Be Afraid to Make Mistakes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Fear of breaking things prevents learning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; Fear of making mistakes is one of the biggest obstacles to learning. If you're afraid to break things, you'll never try new things, and you'll never grow as a developer. Mistakes are a natural part of the learning process. Every developer, from beginners to experts, makes mistakes. The key is to learn from them and move on. Don't be afraid to experiment, try new features, and push the boundaries of what you can create.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Do&lt;/th&gt;
&lt;th&gt;Don't&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;perform()&lt;/code&gt; before &lt;code&gt;start()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;start()&lt;/code&gt; before &lt;code&gt;perform()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Name your display instance &lt;code&gt;display&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use a different variable name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;perform()&lt;/code&gt; before using TileMaps&lt;/td&gt;
&lt;td&gt;Use TileMap without &lt;code&gt;perform()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;deltaTime&lt;/code&gt; for movement&lt;/td&gt;
&lt;td&gt;Use raw numbers for speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Normalize diagonal movement&lt;/td&gt;
&lt;td&gt;Ignore diagonal normalization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;display.add()&lt;/code&gt; for every component&lt;/td&gt;
&lt;td&gt;Forget to add components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;setImage()&lt;/code&gt; for images&lt;/td&gt;
&lt;td&gt;Directly assign image properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;destroy()&lt;/code&gt; for permanent removal&lt;/td&gt;
&lt;td&gt;Hide objects you'll never use again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;hide()&lt;/code&gt; for temporary invisibility&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;destroy()&lt;/code&gt; for temporary removal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Tctxt&lt;/code&gt; for UI text&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Component&lt;/code&gt; for text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;.fixed()&lt;/code&gt; every frame&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;.fixed()&lt;/code&gt; once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;move.bound()&lt;/code&gt; to keep objects on screen&lt;/td&gt;
&lt;td&gt;Write your own edge detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;accelerate()&lt;/code&gt; and &lt;code&gt;decelerate()&lt;/code&gt; for physics&lt;/td&gt;
&lt;td&gt;Directly set speed for physics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Use fake canvas for static content&lt;/td&gt;
&lt;td&gt;Put static content on the main canvas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;fake.refresh()&lt;/code&gt; after changes&lt;/td&gt;
&lt;td&gt;Forget to refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;worldWidth&lt;/code&gt; and &lt;code&gt;worldHeight&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Leave world bounds unset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;Use camera shake for impact&lt;/td&gt;
&lt;td&gt;Overuse camera shake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;camera.follow()&lt;/code&gt; every frame&lt;/td&gt;
&lt;td&gt;Call &lt;code&gt;camera.follow()&lt;/code&gt; once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Set zoom when it changes&lt;/td&gt;
&lt;td&gt;Set zoom every frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;Set fake canvas size to world size&lt;/td&gt;
&lt;td&gt;Sync camera instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;tilemap.crashWith()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use manual tile collision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Use object pooling for high-frequency objects&lt;/td&gt;
&lt;td&gt;Create and destroy objects constantly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;Remove objects from arrays&lt;/td&gt;
&lt;td&gt;Set objects to &lt;code&gt;null&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;clearMargin&lt;/code&gt; optimization&lt;/td&gt;
&lt;td&gt;Keep the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;Preload sounds before &lt;code&gt;display.start()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Play sounds without preloading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;move.sound.*&lt;/code&gt; for quick audio&lt;/td&gt;
&lt;td&gt;Manually handle audio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;console.log()&lt;/code&gt; for debugging&lt;/td&gt;
&lt;td&gt;Leave debug logs in production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;Test on different browsers&lt;/td&gt;
&lt;td&gt;Only test on one browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;Comment your code&lt;/td&gt;
&lt;td&gt;Leave code uncommented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30&lt;/td&gt;
&lt;td&gt;Use meaningful variable names&lt;/td&gt;
&lt;td&gt;Use cryptic names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;Have fun and experiment&lt;/td&gt;
&lt;td&gt;Be afraid to make mistakes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Follow these 31 do's and don'ts to write better, faster, and more reliable Limn Engine games."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — the right way.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>discuss</category>
      <category>intermedate</category>
    </item>
    <item>
      <title>🎮 Building a 3D Game in Limn Engine — Without a 3D Engine</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Mon, 17 Aug 2026 22:33:25 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/building-a-3d-game-in-limn-engine-without-a-3d-engine-5e8b</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/building-a-3d-game-in-limn-engine-without-a-3d-engine-5e8b</guid>
      <description>&lt;h1&gt;
  
  
  🎮 Building a 3D Game in Limn Engine — Without a 3D Engine
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;⚠️ THIS IS AN EXPERIMENTAL APPROACH — USE WITH CAUTION&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Comment if error is found&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Limn Engine is a &lt;strong&gt;2D game engine&lt;/strong&gt; — it was built for 2D games. But what if you want to create a 3D experience without switching to a 3D engine?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The answer:&lt;/strong&gt; You fake it.&lt;/p&gt;

&lt;p&gt;Many classic games created the &lt;strong&gt;illusion of 3D&lt;/strong&gt; using 2D techniques. Games like Doom, Wolfenstein 3D, and Mode 7 racing games all used 2D rendering to simulate 3D worlds.&lt;/p&gt;

&lt;p&gt;In this guide, we'll explore several techniques to create 3D-like experiences in Limn Engine — without a 3D engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The Challenge
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limitation&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No Z-axis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything is 2D — no depth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No 3D rendering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No WebGL, no 3D models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No 3D physics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No collisions in 3D space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No perspective&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No vanishing points by default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The solution:&lt;/strong&gt; Use 2D tricks to simulate 3D depth, perspective, and movement.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Technique 1: Mode 7 (Fake 3D Racing)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Mode 7 was used in games like F-Zero and Mario Kart on the SNES. It creates the illusion of a 3D ground plane by &lt;strong&gt;scaling and rotating&lt;/strong&gt; a 2D image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Instead of a 3D world, you have a 2D image that you transform in real-time. You scale it, rotate it, and move it to simulate movement.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a simple Mode 7 effect with a road image that scales and moves.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── MODE 7 FAKE 3D RACING (EXPERIMENTAL) ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE ROAD IMAGE ──&lt;/span&gt;
&lt;span class="c1"&gt;// In a real game, you'd load an image. For this demo, we draw it.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Draw a simple road with lanes&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Road&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#555&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Lane markings&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLineDash&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Road edges&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strokeStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lineWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLineDash&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lineTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE ROAD COMPONENT ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;road&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toDataURL&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME STATE ──&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_SPEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ACCELERATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FRICTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME LOOP ──&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ── ACCELERATION ──&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// Up arrow to accelerate&lt;/span&gt;
        &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ACCELERATION&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MAX_SPEED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;FRICTION&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ── UPDATE DISTANCE ──&lt;/span&gt;
    &lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── CALCULATE SCALE ──&lt;/span&gt;
    &lt;span class="c1"&gt;// As distance increases, the road gets smaller (simulating distance)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── APPLY TRANSFORM ──&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;newWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;newHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── ROTATE FOR TURNING ──&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;road&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── UI ──&lt;/span&gt;
    &lt;span class="c1"&gt;// (Add speed display, etc.)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 9-31:&lt;/strong&gt; Creates a road image on a temporary canvas&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The road is 200x200 pixels with lane markings&lt;/li&gt;
&lt;li&gt;This is the image that will be transformed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 34:&lt;/strong&gt; &lt;code&gt;const road = new Component(200, 200, null, 400, 300, "image");&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a component that will display the road image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 35:&lt;/strong&gt; &lt;code&gt;road.setImage(canvas.toDataURL());&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Converts the canvas to an image and sets it on the component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 39-41:&lt;/strong&gt; Sets up game state variables&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 47-50:&lt;/strong&gt; Accelerates the car when Up arrow is pressed&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 53:&lt;/strong&gt; &lt;code&gt;distance += speed * dt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increases the distance traveled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 56:&lt;/strong&gt; &lt;code&gt;const scale = Math.max(0.5, 1 + (distance / 1000));&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculates the scale based on distance traveled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 57-58:&lt;/strong&gt; &lt;code&gt;const newWidth = 200 / scale; const newHeight = 200 / scale;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As scale increases, the image gets smaller (simulating distance)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 61-62:&lt;/strong&gt; &lt;code&gt;road.width = newWidth; road.height = newHeight;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applies the new size to the road component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 68-70:&lt;/strong&gt; Rotates the image for turning&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Technique 2: Parallax Scrolling (Fake 3D Depth)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Parallax scrolling creates the illusion of 3D depth by moving background layers at different speeds. This is how many classic 2D games created depth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; You have multiple layers of images. The foreground moves fast, the midground moves medium, and the background moves slow. This simulates the 3D perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a parallax scrolling effect with three layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── PARALLAX SCROLLING (FAKE 3D DEPTH) ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE LAYERS ──&lt;/span&gt;
&lt;span class="c1"&gt;// Layer 1: Background (far mountains) - slowest&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2d5a7a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Layer 2: Midground (hills) - medium&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3a7a3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Layer 3: Foreground (trees) - fastest&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5a8a3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add some trees to foreground (simplified)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2d5a2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME STATE ──&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SPEED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME LOOP ──&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Move offset based on speed&lt;/span&gt;
    &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;SPEED&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── MOVE LAYERS AT DIFFERENT SPEEDS ──&lt;/span&gt;
    &lt;span class="c1"&gt;// Background moves slow (20% speed)&lt;/span&gt;
    &lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Midground moves medium (60% speed)&lt;/span&gt;
    &lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Foreground moves fast (100% speed)&lt;/span&gt;
    &lt;span class="nx"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Keep layers in bounds by wrapping&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;fg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧩 Technique 3: Z-Sorting (Fake 3D Depth Ordering)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;In 3D games, objects closer to the camera are drawn on top of objects further away. You can simulate this in 2D by sorting objects by their "depth" and drawing them in order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Give each object a &lt;code&gt;z&lt;/code&gt; value. The lower the &lt;code&gt;z&lt;/code&gt; value, the further away it is. Sort objects by &lt;code&gt;z&lt;/code&gt; and draw them from back to front.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a scene with objects at different depths, drawn in the correct order.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── Z-SORTING (FAKE 3D DEPTH ORDERING) ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE OBJECTS WITH DEPTH ──&lt;/span&gt;

&lt;span class="c1"&gt;// Far background (z = -10)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2d5a7a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Mountains (z = -5)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mountain1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3a5a3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;mountain1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mountain1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Trees (z = 0)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2d5a2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tree1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tree2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2d5a2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tree2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Player (z = 5)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;520&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Enemy (z = 10) - drawn on top of player if overlapping&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enemy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;530&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── SORT OBJECTS BY Z ──&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sortByZ&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get all components&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Sort by z (lower z first = drawn first = further away)&lt;/span&gt;
    &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Rebuild comm array in sorted order&lt;/span&gt;
    &lt;span class="nx"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;comp&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;comp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME LOOP ──&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Sort objects by z every frame&lt;/span&gt;
    &lt;span class="nf"&gt;sortByZ&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Move enemy to demonstrate overlap&lt;/span&gt;
    &lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;420&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Reverse direction&lt;/span&gt;
        &lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 10-31:&lt;/strong&gt; Creates objects with &lt;code&gt;z&lt;/code&gt; values&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower &lt;code&gt;z&lt;/code&gt; = further away (drawn first)&lt;/li&gt;
&lt;li&gt;Higher &lt;code&gt;z&lt;/code&gt; = closer (drawn last)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 34-44:&lt;/strong&gt; &lt;code&gt;sortByZ()&lt;/code&gt; function&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gets all components from the &lt;code&gt;comm&lt;/code&gt; array&lt;/li&gt;
&lt;li&gt;Sorts them by their &lt;code&gt;z&lt;/code&gt; value&lt;/li&gt;
&lt;li&gt;Rebuilds the &lt;code&gt;comm&lt;/code&gt; array in sorted order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 47-49:&lt;/strong&gt; &lt;code&gt;sortByZ();&lt;/code&gt; called every frame&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensures objects are always drawn in the correct order&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 Technique 4: 2.5D Isometric Projection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Isometric projection is a way to create a 3D look using 2D graphics. Objects are drawn with a 45-degree angle, creating a "3D" view from above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Instead of square tiles, you use diamond-shaped tiles. Objects are drawn with width and height, but positioned using isometric coordinates.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a simple isometric grid with objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── 2.5D ISOMETRIC PROJECTION (EXPERIMENTAL) ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── ISOMETRIC HELPERS ──&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isoToScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isoX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isoY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isoX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;isoY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isoX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;isoY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screenX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screenY&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;screenToIso&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screenY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isoX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isoY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileHeight&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileWidth&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isoX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isoY&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE ISOMETRIC TILES ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create a grid of isometric tiles&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isoToScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Alternate colors for checkerboard&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#3a7a3a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#4a8a4a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Create diamond-shaped tile&lt;/span&gt;
        &lt;span class="c1"&gt;// In a real implementation, you'd use a custom shape or image&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 45 degrees&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── CREATE OBJECT ON ISOMETRIC GRID ──&lt;/span&gt;

&lt;span class="c1"&gt;// Place a player at isometric position (2, 2)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isoToScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playerPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playerPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Place an enemy at isometric position (3, 1)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enemyPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isoToScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enemy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;enemyPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;enemyPos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Comparison of Techniques
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mode 7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Racing games, top-down games&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallax Scrolling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Platformers, side-scrollers&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Z-Sorting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any game with depth&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Isometric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strategy games, RPGs&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💡 Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Combine Techniques
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── USE MULTIPLE TECHNIQUES TOGETHER ──&lt;/span&gt;
&lt;span class="c1"&gt;// Use parallax + z-sorting for a 3D feel&lt;/span&gt;
&lt;span class="c1"&gt;// Use isometric + z-sorting for strategy games&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Use Images for Better Visuals
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── USE IMAGES INSTEAD OF RECTANGLES ──&lt;/span&gt;
&lt;span class="c1"&gt;// Create a cube image for isometric tiles&lt;/span&gt;
&lt;span class="c1"&gt;// Use sprite sheets for different angles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Keep It Simple
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── DON'T OVER-COMPLICATE ──&lt;/span&gt;
&lt;span class="c1"&gt;// Start with one technique and master it&lt;/span&gt;
&lt;span class="c1"&gt;// Add more techniques as needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"You can create 3D-like experiences in Limn Engine using 2D tricks — Mode 7, parallax scrolling, z-sorting, and isometric projection."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — even in 3D.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>development</category>
      <category>gamedev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>🔧 Creating Extensions for Limn Engine — Make Game Development Better and Faster</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Mon, 17 Aug 2026 21:41:55 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/creating-extensions-for-limn-engine-make-game-development-better-and-faster-47hn</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/creating-extensions-for-limn-engine-make-game-development-better-and-faster-47hn</guid>
      <description>&lt;h1&gt;
  
  
  🔧 Creating Extensions for Limn Engine — Make Game Development Better and Faster
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A Complete Guide to Extending Limn Engine with Custom Features&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Limn Engine is already powerful out of the box. But what if you need a feature that doesn't exist yet? What if you want to add a health system, a level editor, or a custom physics behavior?&lt;/p&gt;

&lt;p&gt;The beauty of Limn Engine is that it's &lt;strong&gt;plain JavaScript&lt;/strong&gt;. You can extend it without touching the core engine code. In this guide, we'll show you how to create extensions that make game development faster and more powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Why Create Extensions?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write once, use in multiple projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sharing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Share extensions with the community&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Productivity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build common features faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understand the engine internals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add exactly what you need&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Extension Pattern 1: Prototype Extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;JavaScript allows you to add methods to existing classes using &lt;code&gt;prototype&lt;/code&gt;. This is the simplest way to extend Limn Engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; Every Limn Engine class (like &lt;code&gt;Component&lt;/code&gt;, &lt;code&gt;Display&lt;/code&gt;, &lt;code&gt;Camera&lt;/code&gt;) is a JavaScript class. You can add new methods to these classes that will be available on all instances.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to add a health system to every component. This is useful for players, enemies, and anything else that needs health.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── EXTEND COMPONENT WITH HEALTH SYSTEM ──&lt;/span&gt;

&lt;span class="c1"&gt;// Add health system to every component&lt;/span&gt;
&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHealth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Current health&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// Maximum health&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// Invincibility frames counter&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`💚 Health set to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Damage method&lt;/span&gt;
&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;damage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if invincible&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🛡️ Invincible! No damage taken.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Apply damage&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// 30 frames of invincibility&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if dead&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`💀 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; died!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Returns true if dead&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❤️ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; health remaining`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Returns false if still alive&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Tick invincibility frames&lt;/span&gt;
&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tickInvincible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Draw health bar above component&lt;/span&gt;
&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawHealthBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Don't draw if at max health&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Health percentage (0 to 1)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Bar width matches component width&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                    &lt;span class="c1"&gt;// Bar height&lt;/span&gt;

    &lt;span class="c1"&gt;// Background bar (dark gray)&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Health fill (color based on health)&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#00cc44&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Green — healthy&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ffaa00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Yellow — wounded&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#cc0000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                  &lt;span class="c1"&gt;// Red — critical&lt;/span&gt;

    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// ── USAGE ──&lt;/span&gt;

&lt;span class="c1"&gt;// Create player&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Set health&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHealth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// In the game loop&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Tick invincibility frames&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tickInvincible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// When enemy hits player&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;died&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;died&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Game Over!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Draw health bar&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawHealthBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; &lt;code&gt;Component.prototype.setHealth = function(max) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a new method called &lt;code&gt;setHealth&lt;/code&gt; to every component&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;max&lt;/code&gt; parameter is the maximum health&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 5-6:&lt;/strong&gt; &lt;code&gt;this.hp = max; this.maxHp = max;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets the current health and maximum health to the same value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 7:&lt;/strong&gt; &lt;code&gt;this.invincible = 0;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores invincibility frames counter (0 = not invincible)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 12:&lt;/strong&gt; &lt;code&gt;Component.prototype.damage = function(amount) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a &lt;code&gt;damage&lt;/code&gt; method to every component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 14-16:&lt;/strong&gt; &lt;code&gt;if (this.invincible &amp;gt; 0) { return false; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the component is invincible, no damage is taken&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 19:&lt;/strong&gt; &lt;code&gt;this.hp = Math.max(0, this.hp - amount);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces health, but not below 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 20:&lt;/strong&gt; &lt;code&gt;this.invincible = 30;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes the component invincible for 30 frames&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 23-24:&lt;/strong&gt; &lt;code&gt;if (this.hp === 0) { return true; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Returns &lt;code&gt;true&lt;/code&gt; if the component is dead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 36:&lt;/strong&gt; &lt;code&gt;Component.prototype.tickInvincible = function() {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a method to count down invincibility frames&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 37-39:&lt;/strong&gt; &lt;code&gt;if (this.invincible &amp;gt; 0) { this.invincible--; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces invincibility counter by 1 each frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 42:&lt;/strong&gt; &lt;code&gt;Component.prototype.drawHealthBar = function(ctx) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a method to draw a health bar above the component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 45:&lt;/strong&gt; &lt;code&gt;if (this.hp === this.maxHp) return;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skips drawing if health is full&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 47:&lt;/strong&gt; &lt;code&gt;const pct = this.hp / this.maxHp;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calculates health percentage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 48-49:&lt;/strong&gt; &lt;code&gt;const barW = this.width; const barH = 5;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets the health bar size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 52-58:&lt;/strong&gt; Draws the background and health fill with colors based on health percentage&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Extension Pattern 2: Utility Extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Instead of adding methods to existing classes, you can create your own utility objects. This is cleaner for features that don't naturally belong to a specific class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; You create a new object that contains helper functions. These functions take components as parameters and work on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a &lt;code&gt;level&lt;/code&gt; utility that helps with level design.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── LEVEL UTILITY ──&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Create a simple level from a 2D array&lt;/span&gt;
    &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileColors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tileId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tileId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Skip empty&lt;/span&gt;

                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tileColors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tileId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#555&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;tileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Add walls around the edge of a level&lt;/span&gt;
    &lt;span class="na"&gt;addWalls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Top and bottom walls&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Left and right walls&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wallId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Find empty spaces in a level&lt;/span&gt;
    &lt;span class="na"&gt;findEmpty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;empties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;empties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;empties&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Get a random empty position&lt;/span&gt;
    &lt;span class="na"&gt;randomEmpty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;empties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;empties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;empties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// ── USAGE ──&lt;/span&gt;

&lt;span class="c1"&gt;// Define a level map&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Add walls&lt;/span&gt;
&lt;span class="nx"&gt;myMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addWalls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define tile colors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tileColors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#654321&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Wall&lt;/span&gt;
    &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Coin&lt;/span&gt;
    &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;// Enemy spawn&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Create the level&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileColors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Find a random empty position&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;empty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Empty space at row &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, col &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Place a coin at that position&lt;/span&gt;
    &lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── SAVE AND LOAD LEVELS ──&lt;/span&gt;

&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myLevel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`💾 Level "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" saved!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;myLevel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌ Level "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Save a level&lt;/span&gt;
&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;level1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Load a level later&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadedMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;level1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadedMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✅ Level loaded successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Recreate the level with the loaded map&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadedMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tileColors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; &lt;code&gt;const level = {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a new utility object called &lt;code&gt;level&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 6:&lt;/strong&gt; &lt;code&gt;create: function(map, tileSize, tileColors) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes a 2D array map, tile size, and color mapping&lt;/li&gt;
&lt;li&gt;Creates components for each non-zero tile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 9-14:&lt;/strong&gt; &lt;code&gt;for (let row = 0; row &amp;lt; map.length; row++) { for (let col = 0; col &amp;lt; map[row].length; col++) { const tileId = map[row][col]; if (tileId === 0) continue;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loops through every cell in the map and skips empty cells (0)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 16-22:&lt;/strong&gt; Creates a component for each tile with the right position and color&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 33:&lt;/strong&gt; &lt;code&gt;addWalls: function(map, wallId = 1) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds walls around the edge of a map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 35-37:&lt;/strong&gt; &lt;code&gt;const rows = map.length; const cols = map[0].length;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gets the dimensions of the map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 39-42:&lt;/strong&gt; Adds walls to the top and bottom rows&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 44-47:&lt;/strong&gt; Adds walls to the left and right columns&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 54:&lt;/strong&gt; &lt;code&gt;findEmpty: function(map) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finds all empty cells (value = 0) in a map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 68:&lt;/strong&gt; &lt;code&gt;randomEmpty: function(map) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Returns a random empty position from the map&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 79-82:&lt;/strong&gt; &lt;code&gt;save: function(map, name = "myLevel") { localStorage.setItem(name, JSON.stringify(map)); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saves a level to localStorage as JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 86-90:&lt;/strong&gt; &lt;code&gt;load: function(name = "myLevel") { const data = localStorage.getItem(name); if (!data) { return null; } return JSON.parse(data); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads a level from localStorage&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 Extension Pattern 3: Custom Class Extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Sometimes you need a completely new class that extends Limn Engine's classes. This is useful for creating complex objects with specialized behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; You create a new class that inherits from a Limn Engine class (like &lt;code&gt;Component&lt;/code&gt;) and adds new properties and methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a &lt;code&gt;Player&lt;/code&gt; class that extends &lt;code&gt;Component&lt;/code&gt;. This class will have built-in health, movement, and shooting.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── CUSTOM PLAYER CLASS ──&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5b8cff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Player-specific properties&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastShot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shootCooldown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// seconds&lt;/span&gt;

        &lt;span class="c1"&gt;// Enable physics&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;physics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Update player (called every frame)&lt;/span&gt;
    &lt;span class="nf"&gt;updatePlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Movement&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Shoot&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shoot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Invincibility timer&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Blink effect&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Shoot a bullet&lt;/span&gt;
    &lt;span class="nf"&gt;shoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastShot&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shootCooldown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastShot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bullet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yellow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bullet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Limit bullets&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;old&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Take damage&lt;/span&gt;
    &lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;invincible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1 second at 60fps&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;💀 Player died!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Dead&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Still alive&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Draw health bar&lt;/span&gt;
    &lt;span class="nf"&gt;drawHealth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hp&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxHp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#00cc44&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ffaa00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#cc0000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fillStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barW&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;barH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Update bullets&lt;/span&gt;
    &lt;span class="nf"&gt;updateBullets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── USAGE ──&lt;/span&gt;

&lt;span class="c1"&gt;// Create player&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// In the game loop&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update player&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updatePlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateBullets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawHealth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Check player collision with enemy&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;died&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeDamage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;died&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; &lt;code&gt;class Player extends Component {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a new class called &lt;code&gt;Player&lt;/code&gt; that inherits from &lt;code&gt;Component&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This means it has all Component properties and methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 6:&lt;/strong&gt; &lt;code&gt;constructor(x, y) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The constructor function runs when you create a new Player&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 7:&lt;/strong&gt; &lt;code&gt;super(40, 40, "#5b8cff", x, y, "rect");&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls the parent &lt;code&gt;Component&lt;/code&gt; constructor with size, color, and position&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 10-16:&lt;/strong&gt; &lt;code&gt;this.speed = 200; this.hp = 100; ...&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds player-specific properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 24:&lt;/strong&gt; &lt;code&gt;updatePlayer(dt) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom update method for player logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 26-31:&lt;/strong&gt; &lt;code&gt;this.speedX = 0; ...&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles movement with arrow keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 34-36:&lt;/strong&gt; &lt;code&gt;if (display.keys[32]) { this.shoot(); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks for Space key to shoot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 39-44:&lt;/strong&gt; &lt;code&gt;if (this.invincible &amp;gt; 0) { this.invincible--; this.alpha = 0.5; } else { this.alpha = 1; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages invincibility frames and blink effect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 49:&lt;/strong&gt; &lt;code&gt;shoot() {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates and fires a bullet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 50-51:&lt;/strong&gt; &lt;code&gt;const now = performance.now() / 1000; if (now - this.lastShot &amp;lt; this.shootCooldown) return;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforces a cooldown between shots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 55-59:&lt;/strong&gt; Creates a bullet at the player's position and adds it to the game&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 62-65:&lt;/strong&gt; &lt;code&gt;if (this.bullets.length &amp;gt; 50) { const old = this.bullets.shift(); old.destroy(); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limits the number of bullets to prevent memory issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 70:&lt;/strong&gt; &lt;code&gt;takeDamage(amount) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles damage to the player&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 72:&lt;/strong&gt; &lt;code&gt;if (this.invincible &amp;gt; 0) return false;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ignores damage if invincible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 74:&lt;/strong&gt; &lt;code&gt;this.hp = Math.max(0, this.hp - amount);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces health&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 75:&lt;/strong&gt; &lt;code&gt;this.invincible = 60;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets invincibility for 1 second&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 76:&lt;/strong&gt; &lt;code&gt;display.camera.shake(8, 8);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shakes the camera for impact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 78-80:&lt;/strong&gt; &lt;code&gt;if (this.hp === 0) { return true; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Returns true if dead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 85-96:&lt;/strong&gt; &lt;code&gt;drawHealth(ctx) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Draws a health bar above the player&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 Extension Pattern 4: Scene Management Extension
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;A scene manager helps you organize different game states like menu, gameplay, and game over screens.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a scene manager that handles switching between scenes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── SCENE MANAGER ──&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SceneManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add a scene&lt;/span&gt;
    &lt;span class="nf"&gt;addScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enter&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
            &lt;span class="na"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`📋 Scene "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" added`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Switch to a scene&lt;/span&gt;
    &lt;span class="nf"&gt;switchTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Exit current scene&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// Remove objects from previous scene&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Enter new scene&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌ Scene "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Add objects for new scene&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`▶️ Scene switched to "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Update current scene&lt;/span&gt;
    &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Get current scene name&lt;/span&gt;
    &lt;span class="nf"&gt;getCurrentScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add object to current scene&lt;/span&gt;
    &lt;span class="nf"&gt;addObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentScene&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ── USAGE ──&lt;/span&gt;

&lt;span class="c1"&gt;// Create scene manager&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scenes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SceneManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ── MENU SCENE ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;48px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🎮 MY GAME&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Press SPACE to start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🎮 Menu scene entered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;titleText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;startText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🚪 Menu scene exited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ── GAME SCENE ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🎯 Game scene entered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🚪 Game scene exited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updatePlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateBullets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawHealth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ── START THE GAME ──&lt;/span&gt;
&lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switchTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;menu&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── IN THE GAME LOOP ──&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 4:&lt;/strong&gt; &lt;code&gt;class SceneManager {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a class to manage scenes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 6-9:&lt;/strong&gt; &lt;code&gt;this.scenes = {}; this.currentScene = null; this.objects = {};&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores all scenes, current scene name, and objects per scene&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 12:&lt;/strong&gt; &lt;code&gt;addScene(name, config) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a new scene with a name and configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 13-18:&lt;/strong&gt; &lt;code&gt;this.scenes[name] = { enter: ..., update: ..., exit: ..., objects: ... }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each scene has enter, update, and exit functions, plus a list of objects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 23:&lt;/strong&gt; &lt;code&gt;switchTo(name) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switches to a different scene&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 25-35:&lt;/strong&gt; Exits the current scene and removes its objects&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 37-40:&lt;/strong&gt; Enters the new scene&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 42-48:&lt;/strong&gt; Adds objects for the new scene&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 61:&lt;/strong&gt; &lt;code&gt;addObject(obj) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds an object to the current scene&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📊 Extension Patterns Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prototype Extension&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding simple methods to existing classes&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Utility Extension&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Standalone helper functions&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom Class Extension&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex objects with specialized behavior&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scene Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Organizing game states&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  💡 Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Keep Extensions Modular
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── GOOD: Each extension in its own file ──&lt;/span&gt;
&lt;span class="c1"&gt;// health-extension.js&lt;/span&gt;
&lt;span class="c1"&gt;// level-extension.js&lt;/span&gt;
&lt;span class="c1"&gt;// player-class.js&lt;/span&gt;
&lt;span class="c1"&gt;// scene-manager.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Use Namespaces
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── PREVENT NAMING CONFLICTS ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LimnExtensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Health&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Check for Existing Methods
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ── DON'T OVERRIDE EXISTING METHODS ──&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHealth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHealth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Extensions make Limn Engine infinitely extensible — add health systems, level editors, scene managers, and custom classes without touching the core engine."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — and extend it.&lt;/em&gt; 🔧🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>learning</category>
      <category>tools</category>
    </item>
    <item>
      <title>🏃 The Ultimate Guide to Movement in Limn Engine</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Fri, 07 Aug 2026 23:26:30 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/the-ultimate-guide-to-movement-in-limn-engine-hba</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/the-ultimate-guide-to-movement-in-limn-engine-hba</guid>
      <description>&lt;h1&gt;
  
  
  🏃 The Ultimate Guide to Movement in Limn Engine
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Everything You Need to Know About Moving Objects in Limn Engine&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Movement is the heartbeat of every game. Whether you're dodging bullets in a top-down shooter, leaping across platforms, or guiding a character through a vast open world, how things move defines the player's experience. Get it right, and your game feels smooth, responsive, and alive. Get it wrong, and it feels clunky and frustrating.&lt;/p&gt;

&lt;p&gt;Limn Engine offers a rich toolkit for movement — from simple speed-based motion to complex physics and smooth gliding. In this guide, we'll explore every movement method available, when to use each one, and how they work under the hood.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The Two Core Movement Methods
&lt;/h2&gt;

&lt;p&gt;Before we dive into the specific functions, it's important to understand the two fundamental ways Limn Engine handles movement:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;How It Works&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;move()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moves an object using &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Standard movement — independent of angle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;moveAngle()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moves an object along its current &lt;code&gt;angle&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Directional movement — speed applied relative to angle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These two methods are called automatically by the engine based on the &lt;code&gt;angularMovement&lt;/code&gt; property of your component.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Method 1: Basic Movement with &lt;code&gt;move()&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;move()&lt;/code&gt; is the default movement method in Limn Engine. It works by taking the values stored in &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt; and adding them to the component's &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; position every frame. This is the simplest way to move objects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works internally:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the engine calls &lt;code&gt;move()&lt;/code&gt; on a component, it does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If physics is enabled, it adds gravity to the vertical speed&lt;/li&gt;
&lt;li&gt;It adds &lt;code&gt;speedX&lt;/code&gt; to &lt;code&gt;x&lt;/code&gt; (horizontal movement)&lt;/li&gt;
&lt;li&gt;It adds &lt;code&gt;speedY&lt;/code&gt; to &lt;code&gt;y&lt;/code&gt; (vertical movement)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means you control movement by setting &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt; each frame. If you set &lt;code&gt;speedX = 5&lt;/code&gt;, the object moves 5 pixels right every frame. If you set &lt;code&gt;speedY = -3&lt;/code&gt;, it moves 3 pixels up every frame.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why you need &lt;code&gt;dt&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;dt&lt;/code&gt;, movement speed depends on frame rate — faster computers move faster. By multiplying speed by &lt;code&gt;dt&lt;/code&gt; (delta time), you convert "pixels per frame" to "pixels per second," making movement consistent on all computers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key principle:&lt;/strong&gt; In &lt;code&gt;move()&lt;/code&gt;, you control speed directly. The engine applies it automatically. You don't calculate positions manually — you set speeds.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a player that moves with arrow keys. This is the most common type of movement in games — simple, responsive, and easy to understand. We'll use &lt;code&gt;move()&lt;/code&gt; to handle the actual movement, and we'll use &lt;code&gt;dt&lt;/code&gt; to make it frame-rate independent.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5b8cff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Reset speed each frame&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Arrow keys&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Left&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Right&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Up&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Down&lt;/span&gt;

    &lt;span class="c1"&gt;// Keep player on screen&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 1-4:&lt;/strong&gt; Creates the game window, activates 60fps mode, sets the canvas size to 800x600, and sets the background to dark blue&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 6-7:&lt;/strong&gt; Creates a 40x40 blue square at the center of the screen and adds it to the game world&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 9:&lt;/strong&gt; &lt;code&gt;function update(dt) {&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The game loop function, called 60 times per second&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dt&lt;/code&gt; (delta time) is the time since the last frame, usually about 0.016 seconds&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;dt&lt;/code&gt; makes movement consistent on all computers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 10-11:&lt;/strong&gt; &lt;code&gt;player.speedX = 0; player.speedY = 0;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resets speed to 0 at the start of each frame&lt;/li&gt;
&lt;li&gt;Without this, the player would keep moving forever in the last direction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 13-16:&lt;/strong&gt; &lt;code&gt;if (display.keys[37]) player.speedX = -200 * dt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.keys[37]&lt;/code&gt; checks if the left arrow key is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;37&lt;/code&gt; is the keycode for the left arrow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;player.speedX = -200 * dt&lt;/code&gt; sets horizontal speed to -200 pixels per second (moving left)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;39&lt;/code&gt; is right arrow, &lt;code&gt;200 * dt&lt;/code&gt; means 200 pixels per second to the right&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;38&lt;/code&gt; is up arrow, &lt;code&gt;-200 * dt&lt;/code&gt; means moving up&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;40&lt;/code&gt; is down arrow, &lt;code&gt;200 * dt&lt;/code&gt; means moving down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 18:&lt;/strong&gt; &lt;code&gt;move.bound(player);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps the player on screen by clamping their position to the canvas edges&lt;/li&gt;
&lt;li&gt;If the player tries to go past an edge, it stops them&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧭 Method 2: Angular Movement with &lt;code&gt;moveAngle()&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;moveAngle()&lt;/code&gt; is the second movement method in Limn Engine. Instead of using &lt;code&gt;speedX&lt;/code&gt; and &lt;code&gt;speedY&lt;/code&gt; directly, it applies speed relative to the component's &lt;code&gt;angle&lt;/code&gt; property. This is useful when you want objects to move in the direction they're facing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works internally:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the engine calls &lt;code&gt;moveAngle()&lt;/code&gt; on a component, it does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If physics is enabled, it adds gravity to the vertical speed&lt;/li&gt;
&lt;li&gt;It calculates the horizontal movement: &lt;code&gt;speedX * cos(angle)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It calculates the vertical movement: &lt;code&gt;speedY * sin(angle)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means if you set &lt;code&gt;angle = 45&lt;/code&gt; degrees and &lt;code&gt;speedX = 5&lt;/code&gt;, the object moves diagonally. As the angle changes, the direction of movement changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key principle:&lt;/strong&gt; In &lt;code&gt;moveAngle()&lt;/code&gt;, the speed values are applied relative to the object's facing direction. This makes it perfect for rotating objects, orbiting, and directional movement.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create an object that moves in the direction it's facing. This is useful for orbiting objects, enemies that chase the player, and driving games.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#0a0a10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orbiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cyan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angularMovement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Uses moveAngle() instead of move()&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Rotate continuously&lt;/span&gt;
    &lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 6:&lt;/strong&gt; &lt;code&gt;const orbiter = new Component(20, 20, "cyan", 400, 300, "rect");&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a 20x20 cyan square at the center of the screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 7:&lt;/strong&gt; &lt;code&gt;orbiter.angularMovement = true;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This is the key line.&lt;/strong&gt; Tells the engine to use &lt;code&gt;moveAngle()&lt;/code&gt; instead of &lt;code&gt;move()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;With this set to &lt;code&gt;true&lt;/code&gt;, the component's speed is applied relative to its angle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 8-9:&lt;/strong&gt; &lt;code&gt;orbiter.speedX = 2; orbiter.speedY = 2;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets the speed values that will be applied in the direction the object is facing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 10:&lt;/strong&gt; &lt;code&gt;display.add(orbiter);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds the orbiter to the game world&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 13-14:&lt;/strong&gt; &lt;code&gt;function update(dt) { orbiter.angle += 0.02; }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increases the angle by 0.02 radians each frame&lt;/li&gt;
&lt;li&gt;As the angle changes, the direction of movement changes — creating an orbit&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚗 Method 3: Directional Movement (Car Example)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Directional movement combines rotation with movement. The object can turn and then move forward in its new direction. This is how cars, tanks, and many game characters work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine provides helper functions for directional movement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;move.forward()&lt;/code&gt; — Moves the object in the direction it's facing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.backward()&lt;/code&gt; — Moves the object opposite to the direction it's facing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.turnLeft()&lt;/code&gt; and &lt;code&gt;move.turnRight()&lt;/code&gt; — Rotate the object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions use the object's &lt;code&gt;angle&lt;/code&gt; property internally. &lt;code&gt;move.forward()&lt;/code&gt; calculates the direction using &lt;code&gt;cos(angle)&lt;/code&gt; and &lt;code&gt;sin(angle)&lt;/code&gt;, then moves the object that way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key principle:&lt;/strong&gt; Directional movement separates turning from moving. You turn first, then you move forward. This creates realistic movement like a car.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to build a car that drives forward, backward, and turns. This demonstrates directional movement, rotation, and how to combine movement functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ff6b6b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Enable rotation&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Car controls&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// Up: drive forward&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// Down: reverse&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turnLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Left: turn left&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;turnRight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Right: turn right&lt;/span&gt;

    &lt;span class="c1"&gt;// Keep car on screen&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;car&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 6-8:&lt;/strong&gt; &lt;code&gt;const car = new Component(40, 20, "#ff6b6b", 400, 300, "rect"); car.changeAngle = true; display.add(car);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a 40x20 red rectangle at the center (car shape)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;changeAngle = true&lt;/code&gt; — Enables rotation on the car so it can turn&lt;/li&gt;
&lt;li&gt;Adds the car to the game world&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 11:&lt;/strong&gt; &lt;code&gt;if (display.keys[38]) move.forward(car, 3);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.keys[38]&lt;/code&gt; checks if the up arrow is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.forward(car, 3)&lt;/code&gt; — Moves the car forward 3 pixels in the direction it's facing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 12:&lt;/strong&gt; &lt;code&gt;if (display.keys[40]) move.backward(car, 2);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.keys[40]&lt;/code&gt; checks if the down arrow is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.backward(car, 2)&lt;/code&gt; — Moves the car backward 2 pixels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 13:&lt;/strong&gt; &lt;code&gt;if (display.keys[37]) move.turnLeft(car, 0.05);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.keys[37]&lt;/code&gt; checks if the left arrow is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.turnLeft(car, 0.05)&lt;/code&gt; — Rotates the car left by 0.05 radians (about 3 degrees)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 14:&lt;/strong&gt; &lt;code&gt;if (display.keys[39]) move.turnRight(car, 0.05);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.keys[39]&lt;/code&gt; checks if the right arrow is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.turnRight(car, 0.05)&lt;/code&gt; — Rotates the car right by 0.05 radians&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 16:&lt;/strong&gt; &lt;code&gt;move.bound(car);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps the car on screen&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✨ Method 4: Smooth Movement with &lt;code&gt;glideTo()&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;glideTo()&lt;/code&gt; creates smooth, easing-based movement to a target position. Instead of teleporting or moving at constant speed, the object starts fast and slows down as it approaches its destination. This is called "ease-out" and creates natural-looking motion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works internally:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you call &lt;code&gt;move.glideTo(coin, 1000, 500, 300)&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Records the starting position and the current time&lt;/li&gt;
&lt;li&gt;Each frame, calculates how much time has passed (progress from 0 to 1)&lt;/li&gt;
&lt;li&gt;Applies cubic easing formula: &lt;code&gt;eased = 1 - (1 - progress)³&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Calculates the new position based on the eased value&lt;/li&gt;
&lt;li&gt;Updates the object's position&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The key principle:&lt;/strong&gt; &lt;code&gt;glideTo()&lt;/code&gt; handles all the timing and easing for you. You just specify the target and how long it should take.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to move a coin smoothly to random positions with easing. This creates professional-looking movement without manual lerp functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;infoText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;18px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Press Space to move coin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Move coin to random position when Space is pressed&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glideTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Prevent holding&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 6-7:&lt;/strong&gt; Creates a 30x30 gold square at the center and adds it to the game world&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 9-12:&lt;/strong&gt; Creates a text label at the top-left and locks it to the screen position so it doesn't move with the camera&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 15-19:&lt;/strong&gt; Checks if Space is pressed, then glides the coin to a random position over 1 second&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Method 5: Physics-Based Movement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;Physics-based movement uses acceleration and deceleration to create realistic, weighty motion. Instead of moving at constant speed, objects smoothly speed up and slow down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;move.accelerate()&lt;/code&gt; — Adds acceleration to speed, clamped to a maximum&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.decelerate()&lt;/code&gt; — Reduces speed toward zero without overshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key principle:&lt;/strong&gt; You control acceleration, not speed directly. This creates realistic physics like a car.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to create a player with smooth acceleration and deceleration, like a car. This creates realistic, weighty movement.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5b8cff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Accelerate or decelerate&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// D key&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Accelerate right&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// A key&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Accelerate left&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// Slow down&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Keep player on screen&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Line 9:&lt;/strong&gt; &lt;code&gt;if (display.keys[68]) { move.accelerate(player, 0.6, 0, 8, 0); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks if the D key is pressed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move.accelerate()&lt;/code&gt; — Adds horizontal acceleration at 0.6 per frame, max speed 8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 11:&lt;/strong&gt; &lt;code&gt;else if (display.keys[65]) { move.accelerate(player, -0.6, 0, 8, 0); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks if the A key is pressed&lt;/li&gt;
&lt;li&gt;Negative acceleration moves the player left&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 13:&lt;/strong&gt; &lt;code&gt;else { move.decelerate(player, 0.4, 0); }&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;move.decelerate()&lt;/code&gt; — Reduces horizontal speed toward zero at 0.4 per frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Line 16:&lt;/strong&gt; &lt;code&gt;move.bound(player);&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps the player on screen&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Method 6: Complete Movement Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Concept
&lt;/h3&gt;

&lt;p&gt;In a real game, you'll often need to combine multiple movement methods. This demo shows how different movement techniques can work together in one game.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We're About to Do
&lt;/h3&gt;

&lt;p&gt;We're going to combine multiple movement methods into a single complete game.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a1a2e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── PLAYER ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5b8cff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── ORBITER ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orbiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cyan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angularMovement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ── UI ──&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;infoText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;18px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arrow keys to move, Space to glide, A/D to accelerate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;infoText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ── PLAYER MOVEMENT (Arrow Keys) ──&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ── ORBITER (Angular Movement) ──&lt;/span&gt;
    &lt;span class="nx"&gt;orbiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ── GLIDE (Space Key) ──&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glideTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Each Line Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Lines 6-7:&lt;/strong&gt; Creates the player with basic arrow key movement&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 10-14:&lt;/strong&gt; Creates an orbiter with angular movement that orbits continuously&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 17-20:&lt;/strong&gt; Creates a UI label that stays on screen&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 23-29:&lt;/strong&gt; Player movement with arrow keys and boundary keeping&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 31-32:&lt;/strong&gt; Orbiter rotation — increases angle each frame&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 34-38:&lt;/strong&gt; Glide effect — when Space is pressed, the player glides to a random position&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Pro Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Always Use &lt;code&gt;dt&lt;/code&gt; for Consistent Speed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ CORRECT: Frame-rate independent&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ WRONG: Frame-rate dependent&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; On a fast computer, &lt;code&gt;speedX = 4&lt;/code&gt; moves at 240 pixels per second. On a slow computer, it moves at 120 pixels per second. Using &lt;code&gt;dt&lt;/code&gt; ensures everyone plays at the same speed.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Normalize Diagonal Movement
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;0.707&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// Prevent faster diagonal movement&lt;/span&gt;
    &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;0.707&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;mx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Without normalization, moving diagonally is 1.4 times faster than moving in a single direction. The &lt;code&gt;0.707&lt;/code&gt; factor (which is &lt;code&gt;1/√2&lt;/code&gt;) keeps the speed consistent.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Combine Movement Methods
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Use accelerate for smooth movement + bound to stay on screen&lt;/span&gt;
&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accelerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Use pointTo to aim + forward to move&lt;/span&gt;
&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pointTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enemy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works:&lt;/strong&gt; Each function handles one aspect of movement. Combining them gives you complex behavior with simple code.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Movement Method Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.bound()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps object on screen&lt;/td&gt;
&lt;td&gt;Every game&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.boundTo()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom boundaries&lt;/td&gt;
&lt;td&gt;Arena games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.forward()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move in facing direction&lt;/td&gt;
&lt;td&gt;Driving games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.backward()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move opposite facing direction&lt;/td&gt;
&lt;td&gt;Driving games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.turnLeft()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rotate left&lt;/td&gt;
&lt;td&gt;Driving games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.turnRight()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rotate right&lt;/td&gt;
&lt;td&gt;Driving games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.pointTo()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Face a target&lt;/td&gt;
&lt;td&gt;Aiming, tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.circle()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Orbital motion&lt;/td&gt;
&lt;td&gt;Orbiting enemies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.glideTo()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smooth movement&lt;/td&gt;
&lt;td&gt;Animations, cutscenes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.accelerate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smooth acceleration&lt;/td&gt;
&lt;td&gt;Physics-based movement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.decelerate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smooth deceleration&lt;/td&gt;
&lt;td&gt;Physics-based movement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.project()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Projectile physics&lt;/td&gt;
&lt;td&gt;Arrows, fireballs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.hitObject()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform collision&lt;/td&gt;
&lt;td&gt;Platformers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move.teleport()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Instant position&lt;/td&gt;
&lt;td&gt;Respawns, checkpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Limn Engine offers 15+ movement methods — from simple speed-based movement to complex physics, smooth gliding, and angular motion — so you can build any game mechanic."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — one movement at a time.&lt;/em&gt; 🏃🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>⚔️ Godot vs Limn Engine: Which One Should You Choose?</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Wed, 05 Aug 2026 19:53:12 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/godot-vs-limn-engine-which-one-should-you-choose-2bdl</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/godot-vs-limn-engine-which-one-should-you-choose-2bdl</guid>
      <description>&lt;h1&gt;
  
  
  ⚔️ Godot vs Limn Engine: Which One Should You Choose?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A Brutally Honest Comparison of Two Completely Different Engines&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Godot and Limn Engine are both free, open-source game engines. Both are beginner-friendly. Both can build 2D games.&lt;/p&gt;

&lt;p&gt;But they are &lt;strong&gt;fundamentally different&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Godot is a full-featured, professional-grade engine with a visual editor, node-based architecture, and support for 2D and 3D . Limn Engine is a lightweight, zero‑configuration JavaScript framework that runs entirely in the browser .&lt;/p&gt;

&lt;p&gt;This comparison isn't about which is "better" — it's about helping you choose the right tool for your goals.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Each Engine Is
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Limn Engine
&lt;/h3&gt;

&lt;p&gt;Limn Engine is a &lt;strong&gt;zero‑configuration, browser‑based 2D game framework&lt;/strong&gt;. You drop a single &lt;code&gt;epic.js&lt;/code&gt; file into an HTML page, write a few lines of JavaScript, and your game runs in any browser .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The philosophy:&lt;/strong&gt; "Common outcomes should be functions." Every repetitive task — edge detection, screen shake, UI anchoring — has been turned into a one‑line function .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built on:&lt;/strong&gt; A Toshiba laptop with 4GB RAM, offline W3Schools, and borrowed siblings' laptops. No team. No funding. Just determination .&lt;/p&gt;

&lt;h3&gt;
  
  
  Godot
&lt;/h3&gt;

&lt;p&gt;Godot is a &lt;strong&gt;full‑featured, open‑source game engine&lt;/strong&gt; with a visual editor, node‑based architecture, and support for both 2D and 3D game development . It operates under the MIT license, meaning it is 100% free with no royalties, no subscriptions, and no vendor lock‑in .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The philosophy:&lt;/strong&gt; "Everything is a node." Games are built by composing scenes from reusable building blocks .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built on:&lt;/strong&gt; Over a decade of open‑source development, a large and growing community, and contributions from developers worldwide .&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Head‑to‑Head Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Setup &amp;amp; Installation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — just include &lt;code&gt;epic.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Download and install the editor (~150MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;First Game Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~60 seconds&lt;/td&gt;
&lt;td&gt;~5–10 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser (HTML + JS)&lt;/td&gt;
&lt;td&gt;Desktop application&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn wins for pure speed. You can start coding immediately. Godot requires installation but gives you a full visual editor.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Language &amp;amp; Learning Curve
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;GDScript (Python‑like) or C#&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low–Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type of API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Function‑based&lt;/td&gt;
&lt;td&gt;Node‑based + scripting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn's API is designed to be predictable and intuitive — &lt;code&gt;move.bound()&lt;/code&gt;, &lt;code&gt;camera.shake()&lt;/code&gt;, &lt;code&gt;sound.play()&lt;/code&gt; . Godot's GDScript is beginner‑friendly and Python‑like , but requires understanding nodes, scenes, and signals.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Scene Management
&lt;/h3&gt;

&lt;p&gt;This is where the two engines diverge most dramatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scene Concept&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A number&lt;/td&gt;
&lt;td&gt;A Node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0 seconds&lt;/td&gt;
&lt;td&gt;2–5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Required&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;display.scene = 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get_tree().change_scene_to_file("res://game.tscn")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;Low–Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scene Transitions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Passing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn's scene system is the simplest in any game engine — scenes are just numbers . Godot's system is more powerful but requires understanding nodes and scenes.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Key Features
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2D Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;td&gt;✅ Excellent (native pipeline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3D Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visual Editor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TileMap&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built‑in&lt;/td&gt;
&lt;td&gt;✅ Built‑in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Particle System&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Six presets&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Physics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;✅ Robust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Animation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Sprites&lt;/td&gt;
&lt;td&gt;✅ Full animation system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ SoundManager&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Canvas (CPU)&lt;/td&gt;
&lt;td&gt;OpenGL (GPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ClearMargin Trick&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Unique&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dual‑Renderer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Unique&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Godot has far more features — it's a full engine. Limn has a few unique optimizations that Godot doesn't have .&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Performance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Canvas (CPU)&lt;/td&gt;
&lt;td&gt;OpenGL (GPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Draw Calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optimized with dual‑renderer&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low–Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 FPS → 60 FPS with &lt;code&gt;perform()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;60 FPS with thousands of sprites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn's dual‑renderer system is a clever optimization that drastically reduces draw calls . Godot has GPU acceleration, making it faster for complex scenes.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Platform Support
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;✅ (HTML5 export)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mac&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Android&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;iOS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Godot exports to everything . Limn is web‑only.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Community &amp;amp; Ecosystem
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Growing&lt;/td&gt;
&lt;td&gt;🟢 Large&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tutorials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ DEV.to series&lt;/td&gt;
&lt;td&gt;✅ Thousands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Asset Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Growing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Comprehensive&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Godot has a much larger community and ecosystem . Limn is newer but has passionate developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Godot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup &amp;amp; Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language &amp;amp; Learning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scene Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10/10 (simplicity)&lt;/td&gt;
&lt;td&gt;8/10 (power)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5/10&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overall&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.2/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.8/10&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 Which One Should You Choose?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose Limn Engine If:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You want to build &lt;strong&gt;browser games&lt;/strong&gt; instantly&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;zero configuration&lt;/strong&gt; — just one script tag&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;learn how game engines work&lt;/strong&gt; (the code is open and readable)&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;learn JavaScript&lt;/strong&gt; while making games&lt;/li&gt;
&lt;li&gt;You value &lt;strong&gt;simplicity&lt;/strong&gt; over features&lt;/li&gt;
&lt;li&gt;You want to &lt;strong&gt;ship games fast&lt;/strong&gt; (one HTML file + &lt;code&gt;epic.js&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Godot If:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You want a &lt;strong&gt;full, professional‑grade engine&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;3D support&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You want a &lt;strong&gt;visual editor&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need to export to &lt;strong&gt;multiple platforms&lt;/strong&gt; (Windows, Mac, Linux, mobile)&lt;/li&gt;
&lt;li&gt;You want a &lt;strong&gt;large community&lt;/strong&gt; and ecosystem&lt;/li&gt;
&lt;li&gt;You prefer &lt;strong&gt;Python‑like syntax&lt;/strong&gt; (GDScript)&lt;/li&gt;
&lt;li&gt;You're building a &lt;strong&gt;commercial game&lt;/strong&gt; that needs to ship everywhere&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 The One‑Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Limn Engine is for the web — one file, one script tag, zero setup, instant deployment. Godot is a complete engine — visual editor, 3D support, all platforms, full control."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — with the engine that fits your goals.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>cpp</category>
      <category>python</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Build Your First Browser Game in 60 Seconds with Limn Engine</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Mon, 03 Aug 2026 02:52:35 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/build-your-first-browser-game-in-60-seconds-with-limn-engine-d89</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/build-your-first-browser-game-in-60-seconds-with-limn-engine-d89</guid>
      <description>&lt;h2&gt;
  
  
  🎮 Build Your First Browser Game in 60 Seconds with Limn Engine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Complete Coin Collector Game in Under 100 Lines of JavaScript&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Live Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;See the final result first:&lt;/strong&gt; [&lt;a href="https://limn-engine-doc.vercel.app/test8.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/test8.html&lt;/a&gt;](&lt;a href="http://limn-engine-doc.vercel.app/test8.html%5D%5C(https://limn-engine-doc.vercel.app/test8.html%5C)" rel="noopener noreferrer"&gt;http://limn-engine-doc.vercel.app/test8.html]\(https://limn-engine-doc.vercel.app/test8.html\)&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Play the game, move the blue square with arrow keys, collect all 10 gold coins, and win!&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Most game engines require npm installs, build tools, and hours of setup before you see anything on screen. Limn Engine is different — one script tag, one canvas, and you're ready to build.&lt;/p&gt;

&lt;p&gt;In this tutorial, you'll build a complete coin collector game in under 100 lines of code. You'll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to set up Limn Engine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to create and move a player&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to generate coins at random positions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to detect collisions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to track score and win the game&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to restart with one keypress&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The result:&lt;/strong&gt; A fully playable game that you can share with anyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Step 1: Download Limn Engine
&lt;/h2&gt;

&lt;p&gt;First, you need to get the Limn Engine file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the Limn Engine website: [&lt;a href="http://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app&lt;/a&gt;](&lt;a href="https://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;https://limn-engine-doc.vercel.app&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the download button to get &lt;code&gt;epic.js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Place &lt;code&gt;epic.js&lt;/code&gt; in the same folder as your HTML file&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Limn Engine is a single file. No npm, no build tools, no complex setup. Just one JavaScript file and you're ready to go.&lt;/p&gt;




&lt;h2&gt;
  
  
  📁 Step 2: Create Your HTML File
&lt;/h2&gt;

&lt;p&gt;Create a new HTML file and include the Limn Engine script. This is the foundation of your game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"epic.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// All your game code goes here&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let's break this down so you understand what's happening:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;&lt;/code&gt; — This tells the browser to use the latest version of HTML. Think of it as saying "Hey browser, I'm using modern HTML, please render this correctly."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; — This is the root element of your page. Everything else goes inside it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; — This section contains information about your page that the browser needs before it starts rendering. Things like the title, character set, and in our case, the script we're about to load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;script src="epic.js"&amp;gt;&lt;/code&gt; — This is where we load the Limn Engine. The &lt;code&gt;src&lt;/code&gt; attribute tells the browser to fetch a file called &lt;code&gt;epic.js&lt;/code&gt; from the same folder as your HTML file. This gives you access to all the Limn Engine features like &lt;code&gt;Display&lt;/code&gt;, &lt;code&gt;Component&lt;/code&gt;, &lt;code&gt;Tctxt&lt;/code&gt;, and everything else we'll use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; — This is where the visible content of your page goes. The canvas will be created here automatically when we start the display.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; — This is where we write our game code. The browser will run this code after it loads &lt;code&gt;epic.js&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What happens if you skip this step:&lt;/strong&gt; If you don't include &lt;code&gt;epic.js&lt;/code&gt;, the browser will throw an error saying &lt;code&gt;Display is not defined&lt;/code&gt; because it doesn't know what a Display is.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎮 Step 3: Create the Display
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Display&lt;/code&gt; is your game window. It creates the canvas, manages the game loop, and handles input from the keyboard and mouse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me explain each line so you understand what it's doing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;const display = new Display();&lt;/code&gt; — This creates a new Display object. Think of it as telling the engine "I want a game window." This object will manage everything about your game.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.perform();&lt;/code&gt; — This turns on performance mode. Without this, your game runs at about 50 frames per second. With it, you get 60 frames per second and a special rendering system that makes your game faster. &lt;strong&gt;Always call this before &lt;code&gt;start()&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.start(800, 600);&lt;/code&gt; — This actually creates the canvas on your page. The two numbers tell the engine how big you want the game window: 800 pixels wide and 600 pixels tall. After this line, you'll see a blank canvas on your page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.backgroundColor("green");&lt;/code&gt; — This sets the background color of your canvas. You can use any color name or hex code here. If you skip this, the background will be transparent (white in most browsers).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A helpful way to think about it:&lt;/strong&gt; Creating the Display is like setting up a stage. &lt;code&gt;perform()&lt;/code&gt; is like turning on the lights, &lt;code&gt;start()&lt;/code&gt; is like raising the curtain, and &lt;code&gt;backgroundColor()&lt;/code&gt; is like painting the backdrop.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Step 4: Create the Player
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Component&lt;/code&gt; is any game object — player, enemy, coin, wall. Think of it as anything that appears on screen and can move or be interacted with.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me walk you through what each part means:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;new Component(40, 40, "blue", 400, 300, "rect")&lt;/code&gt; — This creates a new Component. The five pieces of information are:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;40, 40&lt;/code&gt; — The width and height of the component in pixels. This creates a 40×40 square.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"blue"&lt;/code&gt; — The color of the component. This can be a color name, hex code, or even an image path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;400, 300&lt;/code&gt; — The starting position on the screen. This puts the player at the center of an 800×600 canvas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"rect"&lt;/code&gt; — The type of component. This tells the engine to draw a rectangle. Other options include &lt;code&gt;"image"&lt;/code&gt; and &lt;code&gt;"text"&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.add(player);&lt;/code&gt; — This tells the engine to add the player to the game world. Without this line, nothing would appear on screen because the engine wouldn't know about your player.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What if you want to change the player's position?&lt;/strong&gt; You can set &lt;code&gt;player.x&lt;/code&gt; and &lt;code&gt;player.y&lt;/code&gt; to any number. For example, &lt;code&gt;player.x = 100;&lt;/code&gt; moves the player to the left edge.&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 Step 5: Create the UI (Score and Win Text)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Tctxt&lt;/code&gt; is a special text component that gives you more control over how text looks. It can have backgrounds, padding, alignment, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0 / 10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;winText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;48px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOU WIN!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;restartText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;20px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Press R to play again&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me explain each piece:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score Text:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;new Tctxt("24px", "Arial", "white", 20, 50)&lt;/code&gt; — Creates text at position (20, 50) from the top-left corner. The font is 24 pixels tall and uses the Arial font in white color.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.setText("Score: 0 / 10")&lt;/code&gt; — Sets the actual text content. This is separate from creating the object so you can update it later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.add(scoreText)&lt;/code&gt; — Adds it to the game world so it appears on screen.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Win Text (hidden at start):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.align = "center"&lt;/code&gt; — Centers the text horizontally. This is why the x position is 400 (the center of an 800-pixel screen).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.hide()&lt;/code&gt; — Makes the text invisible at the start. This is important because the player hasn't won yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.show()&lt;/code&gt; — Will be called later when the player wins.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Restart Text:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Similar to the win text, but positioned slightly lower at y=380.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also hidden at start and will appear when the player wins.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why use &lt;code&gt;Tctxt&lt;/code&gt; instead of a regular Component?&lt;/strong&gt; &lt;code&gt;Tctxt&lt;/code&gt; gives you features like alignment, background color, and padding that normal text components don't have. This makes your UI look much more polished.&lt;/p&gt;




&lt;h2&gt;
  
  
  🪙 Step 6: Create the Coins
&lt;/h2&gt;

&lt;p&gt;Coins are also Components. We'll create 10 coins at random positions using a loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;coins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me walk through what's happening:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;const totalCoins = 10;&lt;/code&gt; — This sets how many coins we want. We use a variable so we can change it easily later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;let coins = [];&lt;/code&gt; — This creates an empty array to store all the coin objects. We need this so we can check collisions with each coin and remove collected ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;for (let i = 0; i &amp;lt; totalCoins; i++)&lt;/code&gt; — This loop runs 10 times. Each time, it creates one new coin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Math.random() * 700 + 50&lt;/code&gt; — This creates a random number between 50 and 750. Why 50 and 750? We want the coins to appear away from the edges so they're reachable. The canvas is 800 pixels wide, so 50 and 750 give a nice margin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Math.random()&lt;/code&gt; gives a number between 0 and 1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multiply by 700 gives a number between 0 and 700.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add 50 gives a number between 50 and 750.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Math.random() * 500 + 50&lt;/code&gt; — Same logic for the Y position. The canvas is 600 pixels tall, so this gives numbers between 50 and 550.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.add(coin)&lt;/code&gt; — Adds the coin to the game world.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;coins.push(coin)&lt;/code&gt; — Stores the coin in our array for later use.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What if you want more coins?&lt;/strong&gt; Just change &lt;code&gt;totalCoins&lt;/code&gt; to any number. The loop will create that many coins.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Step 7: Game State
&lt;/h2&gt;

&lt;p&gt;We need variables to track the game status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What these mean:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;score&lt;/code&gt; — This starts at 0 and increases by 1 each time you collect a coin. It's what we show on the score text.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameActive&lt;/code&gt; — This is a boolean (true/false). When &lt;code&gt;true&lt;/code&gt;, the game updates normally. When &lt;code&gt;false&lt;/code&gt;, the game stops. This prevents the player from continuing after winning.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why do we need &lt;code&gt;gameActive&lt;/code&gt;?&lt;/strong&gt; Without it, the player could keep moving and the score could change even after winning. It gives us control over when the game ends.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 Step 8: The Game Loop
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;update()&lt;/code&gt; function runs every frame, which is 60 times per second. This is where all the action happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="c1"&gt;// Restart with R key&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;gameActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me explain the flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;function update(deltaTime)&lt;/code&gt; — This is where Limn Engine calls your game code every frame. The &lt;code&gt;deltaTime&lt;/code&gt; parameter tells you how much time has passed since the last frame (usually about 0.016 seconds at 60fps).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;if (display.keys[82])&lt;/code&gt; — The &lt;code&gt;display.keys&lt;/code&gt; array stores which keys are currently pressed. &lt;code&gt;82&lt;/code&gt; is the keycode for the letter 'R'. When you press R, this condition becomes true.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;location.reload();&lt;/code&gt; — This reloads the page, which restarts the game. It's a simple way to reset everything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;return;&lt;/code&gt; — This exits the update function early. Without this, the game would keep running even as it reloads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;if (!gameActive) return;&lt;/code&gt; — If the game is not active (because you've won), this exits the function early. This stops the player from moving or collecting more coins after winning.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What is keycode 82?&lt;/strong&gt; Every key on the keyboard has a numeric keycode. 82 is 'R', 37-40 are arrow keys, 32 is space. You can find a full list of keycodes online.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⌨️ Step 9: Player Movement
&lt;/h2&gt;

&lt;p&gt;Now we add the controls to move the player with arrow keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Player movement&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's what each part does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.keys[37]&lt;/code&gt; — Keycode 37 is the left arrow key. If it's pressed, we set the player's horizontal speed to negative (move left).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-400 * deltaTime&lt;/code&gt; — This moves the player at 400 pixels per second. Multiplying by &lt;code&gt;deltaTime&lt;/code&gt; makes the movement smooth and consistent regardless of frame rate. On a fast computer, &lt;code&gt;deltaTime&lt;/code&gt; is smaller, so the player moves less per frame but more often. On a slow computer, &lt;code&gt;deltaTime&lt;/code&gt; is larger, so the player moves more per frame but less often. The result is the same speed on both.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;move.bound(player);&lt;/code&gt; — This keeps the player inside the canvas boundaries. Without this, the player would walk off the screen and disappear. It's like having an invisible wall at the edges.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Keycodes explained:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;37&lt;/code&gt; = Left arrow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;39&lt;/code&gt; = Right arrow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;38&lt;/code&gt; = Up arrow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;40&lt;/code&gt; = Down arrow&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🪙 Step 10: Coin Collection
&lt;/h2&gt;

&lt;p&gt;This is where we check if the player touches a coin and handle what happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Coin collection&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//display.camera).shake(3, 3);&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Let me break this down carefully:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;for (let i = 0; i &amp;lt; coins.length; i++)&lt;/code&gt; — This loops through every coin in our array. We use &lt;code&gt;i&lt;/code&gt; to track which coin we're looking at.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;if (player.crashWith(coins[i]))&lt;/code&gt; — This checks if the player overlaps with the current coin. &lt;code&gt;crashWith()&lt;/code&gt; is a built-in collision detection function. It returns &lt;code&gt;true&lt;/code&gt; if two components are touching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;display.camera.shake(3, 3);&lt;/code&gt; — This makes the screen shake slightly. The two numbers are the intensity on the X and Y axes. A small shake like this gives satisfying feedback to the player.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;coins[i].hide();&lt;/code&gt; — This makes the coin invisible. It's still in memory but not drawn on screen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;coins.splice(i, 1);&lt;/code&gt; — This removes the coin from the array. &lt;code&gt;splice(i, 1)&lt;/code&gt; means "remove 1 item at position &lt;code&gt;i&lt;/code&gt;".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;score++;&lt;/code&gt; — This adds 1 to the score.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;scoreText.setText("Score: " + score + " / " + totalCoins);&lt;/code&gt; — This updates the score display.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;break;&lt;/code&gt; — This exits the loop early. Why? Because we've already found and collected a coin, so we don't need to check the rest of the coins this frame.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What happens if you don't use &lt;code&gt;break&lt;/code&gt;?&lt;/strong&gt; The player could collect multiple coins in one frame if they overlap with several at once, which would feel unfair and break the game.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏆 Step 11: Win Condition
&lt;/h2&gt;

&lt;p&gt;Finally, we check if the player has collected all the coins.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Win condition&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;gameActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="nx"&gt;gameActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//winText.show)();&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//restartText.show)();&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here's the logic:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;if (score === totalCoins &amp;amp;&amp;amp; gameActive)&lt;/code&gt; — This checks two things:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The score equals the total number of coins (10)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The game is still active&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameActive = false;&lt;/code&gt; — This stops the game. The player won't be able to move or collect more coins.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;winText.show();&lt;/code&gt; — This makes the "YOU WIN!" text appear.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;restartText.show();&lt;/code&gt; — This makes the "Press R to play again" text appear.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;scoreText.hide();&lt;/code&gt; — This hides the score display since you don't need it anymore.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why do we check &lt;code&gt;gameActive&lt;/code&gt;?&lt;/strong&gt; Without this check, the win condition could trigger multiple times. Once you win, the game is no longer active, so the condition won't run again.&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 Complete Code
&lt;/h2&gt;

&lt;p&gt;Here's the entire game in one file. You can copy this and run it immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"epic.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;restartText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;20px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;coins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// Player&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Score text&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0 / &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Win text (hidden at start)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;winText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;48px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOU WIN!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;align&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Press R to play again&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create coins&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;coin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gold&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="c1"&gt;// Restart with R key&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="nf"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;gameActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Player movement&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;deltaTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speedY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Coin collection&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//display.camera).shake(3, 3);&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; / &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Win condition&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;totalCoins&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;gameActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="nx"&gt;gameActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;winText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//winText.show)();&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;restartText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//restartText.show)();&lt;/span&gt;

&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 What You've Learned
&lt;/h2&gt;

&lt;p&gt;| Concept | Why It Matters | How It Works |&lt;/p&gt;

&lt;p&gt;|---------|----------------|--------------|&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Limn Engine Setup&lt;/strong&gt; | No npm, no build tools — just one script tag | Include &lt;code&gt;epic.js&lt;/code&gt; and start coding |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Components&lt;/strong&gt; | Every game object is a Component | &lt;code&gt;new Component()&lt;/code&gt; creates one |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Player Movement&lt;/strong&gt; | Arrow keys with &lt;code&gt;deltaTime&lt;/code&gt; for consistent speed | &lt;code&gt;display.keys&lt;/code&gt; reads input, &lt;code&gt;speedX/speedY&lt;/code&gt; moves |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Collision Detection&lt;/strong&gt; | &lt;code&gt;crashWith()&lt;/code&gt; detects overlaps | Returns &lt;code&gt;true&lt;/code&gt; when two components touch |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Random Placement&lt;/strong&gt; | &lt;code&gt;Math.random()&lt;/code&gt; creates variety | Gives different positions every time |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;UI with Tctxt&lt;/strong&gt; | Styled text with background and padding | Supports alignment, colors, and show/hide |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Win Condition&lt;/strong&gt; | Game state management | &lt;code&gt;gameActive&lt;/code&gt; controls the game flow |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Restart&lt;/strong&gt; | One keypress to reload | &lt;code&gt;location.reload()&lt;/code&gt; resets everything |&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 What's Next: Article 2 — "Build a Top-Down Shooter with Particles, AI, and Screen Shake"
&lt;/h2&gt;

&lt;p&gt;Now that you've built your first game, it's time to level up. In the next tutorial, you'll build a &lt;strong&gt;complete top-down space shooter&lt;/strong&gt; that demonstrates more advanced Limn Engine features.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You'll Build
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
┌─────────────────────────────────────────────────────────────────┐

│ │

│ Score: 0 Lives: 3 Bullets: 12 │

│ │

│ 👾 (Enemy) │

│ │

│ 🟦 (Player) │

│ ↑ │

│ (WASD to move) │

│ (Space to shoot) │

│ │

│ ✨ (Particle trail behind player) │

│ │

└─────────────────────────────────────────────────────────────────┘

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You'll Learn
&lt;/h3&gt;

&lt;p&gt;| Concept | Why It Matters |&lt;/p&gt;

&lt;p&gt;|---------|----------------|&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Particle Systems&lt;/strong&gt; | Visual effects make games feel alive |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Enemy AI&lt;/strong&gt; | Enemies that chase the player |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Bullet Management&lt;/strong&gt; | Limiting ammunition for balance |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Screen Shake&lt;/strong&gt; | Impact feedback |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Invincibility Frames&lt;/strong&gt; | Player protection after being hit |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Game States&lt;/strong&gt; | Lives, score, game over |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;Camera Follow&lt;/strong&gt; | Keeping the player centered |&lt;/p&gt;

&lt;p&gt;| &lt;strong&gt;UI with Tctxt&lt;/strong&gt; | Displaying lives and bullets |&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Functions You'll Use
&lt;/h3&gt;

&lt;p&gt;| Function | Purpose |&lt;/p&gt;

&lt;p&gt;|----------|---------|&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;display.camera.follow()&lt;/code&gt; | Camera follows player |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;move.bound()&lt;/code&gt; | Keep player on screen |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;component.crashWith()&lt;/code&gt; | Collision detection |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;move.particles.explosion()&lt;/code&gt; | Explosion effects |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;move.particles.blood()&lt;/code&gt; | Blood effects |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;display.camera.shake()&lt;/code&gt; | Screen shake |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;particles.emit()&lt;/code&gt; | Custom particle effects |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;component.destroy()&lt;/code&gt; | Remove objects |&lt;/p&gt;

&lt;p&gt;| &lt;code&gt;display.stop()&lt;/code&gt; | End the game |&lt;/p&gt;

&lt;h2&gt;
  
  
  🎮 Preview the Game
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://limn-engine-doc.vercel.app/test9.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/test9.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Play the game, move with WASD or arrow keys, shoot with Space, and survive as long as possible!&lt;/p&gt;




&lt;h2&gt;
  
  
  🎮 Controls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WASD or arrow keys&lt;/strong&gt; — Move the player&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Space&lt;/strong&gt; — Shoot (limited bullets, auto-recharge)&lt;/li&gt;
&lt;li&gt;Enemies chase the player&lt;/li&gt;
&lt;li&gt;Collect score by destroying enemies&lt;/li&gt;
&lt;li&gt;Lose lives when enemies touch you&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live Demo (Article 1)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/test8.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/test8.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live Demo (Article 2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/test9.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/test9.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Download Limn Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limn Engine Docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Beginner Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/beginner.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/beginner.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discord Community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://discord.gg/ZqnUtTQb8" rel="noopener noreferrer"&gt;discord.gg/ZqnUtTQb8&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — one line of code at a time.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>🔥 Limn Engine vs Python Arcade — Which One Should You Learn?</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Fri, 31 Jul 2026 15:22:12 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/limn-engine-vs-python-arcade-which-one-should-you-learn-42cg</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/limn-engine-vs-python-arcade-which-one-should-you-learn-42cg</guid>
      <description>&lt;h1&gt;
  
  
  🔥 Limn Engine vs Python Arcade — Which One Should You Learn?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A Brutally Honest Comparison of Two Beginner-Friendly Game Engines&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;If you're a beginner looking to build 2D games, two names keep coming up: &lt;strong&gt;Limn Engine&lt;/strong&gt; and &lt;strong&gt;Python Arcade&lt;/strong&gt;. Both are designed for beginners. Both are free. Both make game development accessible.&lt;/p&gt;

&lt;p&gt;But they are &lt;strong&gt;fundamentally different&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One runs in your browser with JavaScript. The other runs on your desktop with Python. One is a single file you drop into an HTML page. The other requires a Python environment and installation.&lt;/p&gt;

&lt;p&gt;This comparison is about helping you choose the right engine for your goals — not about which is "better." Because "better" depends entirely on what you want to build and how you want to build it.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Each Engine Is
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Limn Engine
&lt;/h3&gt;

&lt;p&gt;Limn Engine is a &lt;strong&gt;lightweight, zero-configuration 2D game engine for the browser&lt;/strong&gt;. You drop a single &lt;code&gt;epic.js&lt;/code&gt; file into an HTML page, write a few lines of JavaScript, and your game runs in any browser — no npm, no build step, no complex tooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The philosophy:&lt;/strong&gt; "Common outcomes should be functions." Things you do in every game — edge detection, screen shake, particle effects — are built-in as one-line functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Python Arcade
&lt;/h3&gt;

&lt;p&gt;Arcade is an &lt;strong&gt;easy-to-learn Python library for creating 2D games&lt;/strong&gt;. It's built on top of pyglet and OpenGL, and it's designed for programmers who want to make games without learning a complex framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The philosophy:&lt;/strong&gt; "Friendly API that caters to both beginners and experts." Arcade provides a clean, modern Python API with full type hints and excellent documentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Head-to-Head Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Setup &amp;amp; Installation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — just include &lt;code&gt;epic.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install arcade&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero — no npm, no build tools&lt;/td&gt;
&lt;td&gt;Python, pyglet, OpenGL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;First Game Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~60 seconds&lt;/td&gt;
&lt;td&gt;~5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser (HTML + JS)&lt;/td&gt;
&lt;td&gt;Python (desktop)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn Engine wins here because there's literally no setup — just include a script tag. Arcade requires Python installation, which adds friction for absolute beginners.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Language &amp;amp; Learning Curve
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Target Audience&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Absolute beginners&lt;/td&gt;
&lt;td&gt;Beginners &amp;amp; educators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Low-medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type Hints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Both are beginner-friendly. Limn Engine's API is designed to be intuitive with one-liners for common tasks. Arcade has better type hints and Python's readability.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Key Features
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTML5 Canvas&lt;/td&gt;
&lt;td&gt;OpenGL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Camera Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Physics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Basic (gravity, collision)&lt;/td&gt;
&lt;td&gt;Simple physics engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TileMap Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Particle System&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Six presets&lt;/td&gt;
&lt;td&gt;❌ No built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprite Animation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ AnimatedSprite&lt;/td&gt;
&lt;td&gt;✅ TextureAnimationSprite&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audio&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Screen Shake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ One-line function&lt;/td&gt;
&lt;td&gt;❌ No built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visual Editor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Limn Engine has more built-in "polish" features (particles, screen shake) that Arcade lacks. Arcade has more robust physics, but Limn's feature set is better for beginners who want quick visual feedback.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Performance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rendering Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Canvas (CPU)&lt;/td&gt;
&lt;td&gt;OpenGL (GPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprite Scaling/Rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Canvas-based&lt;/td&gt;
&lt;td&gt;GPU-accelerated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static Draw Calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One per frame (dual-canvas)&lt;/td&gt;
&lt;td&gt;Depends on implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 FPS → 60 FPS with &lt;code&gt;perform()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;60 FPS with thousands of sprites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Arcade has an edge for complex scenes (OpenGL acceleration). Limn Engine's dual-canvas system is optimized for static backgrounds, but it's still CPU-bound.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Memory Management
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic GC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ JavaScript garbage collector&lt;/td&gt;
&lt;td&gt;✅ Python garbage collector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Object Pooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Built-in (custom)&lt;/td&gt;
&lt;td&gt;✅ Available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Leak Prevention&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;destroy()&lt;/code&gt; method&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;remove()&lt;/code&gt; method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprite Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual via arrays&lt;/td&gt;
&lt;td&gt;SpriteList container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Images loaded once&lt;/td&gt;
&lt;td&gt;Automatic resource cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Arcade's &lt;code&gt;SpriteList&lt;/code&gt; and &lt;code&gt;cleanup()&lt;/code&gt; methods make resource management easier. Limn Engine requires manual &lt;code&gt;destroy()&lt;/code&gt; calls, which gives you more control but also more responsibility.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Platform Support
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;❌ Not yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mac&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linux&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Raspberry Pi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No (Pi 4/5 only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; This is the &lt;strong&gt;biggest difference&lt;/strong&gt;. Limn Engine is web-native, which is great for instant deployment. Arcade runs on Windows, Mac, and Linux — three platforms instead of one.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Community &amp;amp; Documentation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Comprehensive&lt;/td&gt;
&lt;td&gt;✅ Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟡 Growing&lt;/td&gt;
&lt;td&gt;🟢 Established&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tutorials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ DEV.to series&lt;/td&gt;
&lt;td&gt;✅ Real Python, official docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; Both are well-documented. Arcade has a larger, more established community.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Overall Scores
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Limn Engine&lt;/th&gt;
&lt;th&gt;Python Arcade&lt;/th&gt;
&lt;th&gt;Winner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup &amp;amp; Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10/10&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;🟢 Limn Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language &amp;amp; Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;8.5/10&lt;/td&gt;
&lt;td&gt;🟢 Limn Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;7.5/10&lt;/td&gt;
&lt;td&gt;🟢 Limn Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;🟢 Python Arcade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;🟢 Python Arcade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;🟢 Python Arcade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community &amp;amp; Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;🟢 Python Arcade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overall Average&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.4/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.6/10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Python Arcade (slightly)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 Which One Should You Choose?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose Limn Engine If:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If You Want To...&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build browser games&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limn Engine runs natively in the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learn JavaScript&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You'll write real JavaScript, not a visual language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ship games instantly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One HTML file + &lt;code&gt;epic.js&lt;/code&gt; = deploy to any web host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No npm, no Python, no build tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Understand game engines&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The code is open and readable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build quickly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-liners for common tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Choose Python Arcade If:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If You Want To...&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build desktop games&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Arcade runs on Windows, Mac, Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learn Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python is beginner-friendly and widely used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Work with OpenGL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GPU-accelerated graphics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create complex games&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;More robust physics and sprite handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use established libraries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large ecosystem of Python tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No web deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You don't need to ship to the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Limn Engine is for the web — one file, one script tag, zero setup, instant deployment. Python Arcade is for the desktop — Python, pip, OpenGL, full control."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — with the engine that fits your goals.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>python</category>
      <category>discuss</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>🎮 Top 10 2D JavaScript Game Engines for Beginners (Ranked)</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Mon, 27 Jul 2026 12:34:33 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/top-10-2d-javascript-game-engines-for-beginners-ranked-1b9i</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/top-10-2d-javascript-game-engines-for-beginners-ranked-1b9i</guid>
      <description>&lt;h1&gt;
  
  
  🎮 Top 10 2D JavaScript Game Engines for Beginners (Ranked)
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A Brutally Honest Look at What Works and What Doesn't&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;Let's be real: most "beginner-friendly" game engines are either too simple to build anything useful or too complex to actually learn from. The game development community loves to recommend the same few engines over and over, but they rarely tell you the downsides.&lt;/p&gt;

&lt;p&gt;This list is different. We're ranking the top 10 2D JavaScript game engines for beginners — but we're also telling you what's wrong with each one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No marketing fluff. Just honesty.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Makes an Engine "Beginner-Friendly"?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Easy setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No complex build tools or configuration files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Good documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clear explanations and examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Active community&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Help when you get stuck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forgiving API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not too many ways to do the same thing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quick results&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;See something on screen fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No hidden costs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free and open-source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  #10 — Pixi.js
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 7/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; High-performance 2D rendering&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Pixi.js is a 2D rendering library — not a full game engine. It provides high-speed 2D rendering using WebGL with Canvas fallback. It's used by Disney, Facebook, and Adobe.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;Pixi.js is at the bottom because &lt;strong&gt;it's not a game engine&lt;/strong&gt;. It's a rendering library. If you want to build a game, you'll need to build everything else yourself — physics, input handling, audio, game loop, scene management. This is overwhelming for beginners. The steep learning curve and the fact that you have to piece together multiple libraries make it the least beginner-friendly option on this list.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazing fast&lt;/strong&gt; — One of the fastest 2D rendering engines available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Used by big companies&lt;/strong&gt; — Proven in production at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excellent rendering&lt;/strong&gt; — WebGL-accelerated with fallback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great API&lt;/strong&gt; — Well-designed and easy to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong community&lt;/strong&gt; — Large user base with many resources&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Not a Game Engine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pixi.js is a rendering library, not a game engine. It doesn't include physics, input handling, audio, or a game loop. You'll need to build these systems yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Steep Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because you have to build so much yourself, the learning curve is steep. You'll need to understand rendering, game loops, and system design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. More Code to Write&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything takes more code. Want to handle input? You'll need to write your own system. Want physics? You'll need to integrate a physics library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Fragmented Ecosystem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's no single "right way" to build a game with Pixi.js. You'll need to piece together different libraries and frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Overkill for Simple Games&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're building a simple game, Pixi.js is overkill. You'll spend more time setting up infrastructure than building your game.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Developers who want full control&lt;/li&gt;
&lt;li&gt;✅ Developers who already have their own game logic&lt;/li&gt;
&lt;li&gt;✅ Developers building performance-critical applications&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Absolute beginners&lt;/li&gt;
&lt;li&gt;❌ Developers who want a complete engine&lt;/li&gt;
&lt;li&gt;❌ Anyone building a simple game&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #9 — PlayCanvas (2D Mode)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 6.5/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Teams building 2D games in the browser with visual editing&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;PlayCanvas is a web-based game engine that, while primarily known for 3D, can also be used for 2D game development through its 2D sprite system. It features a visual editor, real-time collaboration, and WebGL rendering.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;PlayCanvas is a 3D-first engine. The 2D features feel like an afterthought. The editor is cluttered with 3D-specific options that beginners don't need. The free tier is limited, and the cloud dependence means you can't work offline. For pure 2D game development, there are better, more focused options.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual editor&lt;/strong&gt; — Browser-based editor with real-time preview&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration&lt;/strong&gt; — Multiple people can work on the same project simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No software to install&lt;/strong&gt; — Everything runs in the browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good for 2D&lt;/strong&gt; — Includes sprites, tilemaps, and 2D physics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export options&lt;/strong&gt; — Export to web, mobile, and desktop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asset management&lt;/strong&gt; — Built-in asset pipeline&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. 3D-First Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PlayCanvas is built for 3D first. The 2D features, while functional, feel like an afterthought. The editor is cluttered with 3D-specific options that beginners don't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Pricing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PlayCanvas has a free tier with limitations. For advanced features or private projects, you'll need to pay. This can be a dealbreaker for beginners.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cloud Dependence&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is cloud-based. If you lose internet access or the service goes down, you can't work. This also means your project files are stored on their servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Limited Free Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The free tier is limited in terms of storage, private projects, and features. To unlock full capabilities, you'll need to subscribe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 3D-first design makes the learning curve steeper than it needs to be for 2D development. You'll spend time filtering out 3D features you don't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. 2D Physics is Basic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 2D physics support (based on ammo.js) is functional but not as polished as dedicated 2D engines.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Beginners interested in both 2D and 3D&lt;/li&gt;
&lt;li&gt;✅ Teams working together remotely&lt;/li&gt;
&lt;li&gt;✅ Developers who want a cloud-based workflow&lt;/li&gt;
&lt;li&gt;✅ Developers who like visual editing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Developers building pure 2D games&lt;/li&gt;
&lt;li&gt;❌ Developers who prefer offline development&lt;/li&gt;
&lt;li&gt;❌ Anyone on a tight budget&lt;/li&gt;
&lt;li&gt;❌ Beginners who want a focused 2D experience&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #8 — MelonJS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 6.5/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Building lightweight games quickly&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;MelonJS is a lightweight, open-source 2D game engine that tries to be simple and fast. It includes a basic set of features: sprite handling, audio, input, and a simple scene system.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;MelonJS is lightweight, but that comes at a cost. The engine lacks many features that you'd expect from a modern engine. Physics is basic. There's no built-in particle system. Animation support is minimal. The documentation is outdated, and the community is small. For a beginner, this means more time solving problems alone.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extremely lightweight&lt;/strong&gt; — The core engine is small and fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple API&lt;/strong&gt; — The code is straightforward and easy to understand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No build tools needed&lt;/strong&gt; — Just include the script and start coding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good for mobile&lt;/strong&gt; — Optimized for performance on mobile devices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean codebase&lt;/strong&gt; — The source code is well-organized and readable&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Limited Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MelonJS lacks many features that you'd expect from a modern engine. Physics is basic at best. There's no built-in particle system, and animation support is minimal. You'll spend a lot of time building systems that other engines provide out of the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Small Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The community is significantly smaller than Phaser or Godot. Finding tutorials, answers, or plugins is challenging. For a beginner, this means more time solving problems alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Outdated Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The official documentation is sparse and often outdated. Many examples no longer work with the latest version. This can be incredibly frustrating when you're trying to learn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dated Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design philosophy feels stuck in the past. While other engines have evolved, MelonJS has remained mostly static. It works, but it doesn't feel modern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Limited Platform Support&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Export options are limited. You're mostly stuck with the web unless you want to build your own export pipeline.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Developers who want a lightweight, fast engine&lt;/li&gt;
&lt;li&gt;✅ Beginners building simple games&lt;/li&gt;
&lt;li&gt;✅ Developers comfortable with minimal features&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Beginners who want a full-featured engine&lt;/li&gt;
&lt;li&gt;❌ Developers building complex games&lt;/li&gt;
&lt;li&gt;❌ Anyone who needs modern features like particles or advanced physics&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #7 — CT.js
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 6/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Beginners who want a visual editor but still want to code&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;CT.js is a 2D game engine that tries to be everything: visual editor, code editor, asset manager, and export tool all in one. It's built on Pixi.js for rendering and includes a visual editor that's somewhere between Construct and a code editor.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;CT.js tries to do too much and does none of it well. The visual editor is useful for placing objects, but anything beyond basic positioning requires writing code. The documentation is limited, and the community is small. The learning curve is confusing because you're mixing visual and code approaches.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual editor&lt;/strong&gt; — You can see your game objects and levels laid out visually&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in tools&lt;/strong&gt; — Asset manager, sprite editor, sound manager all included&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export options&lt;/strong&gt; — Export to web, Windows, Mac, and Linux&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One download&lt;/strong&gt; — Everything you need in one package&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beginner-friendly UI&lt;/strong&gt; — The interface is cleaner than most visual editors&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. The Visual Editor is Half-Baked&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The visual editor is useful for placing objects, but anything beyond basic positioning requires writing code. This creates a frustrating split: you're using a visual tool but still need to understand the code. It's the worst of both worlds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Limited Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The documentation covers the basics but falls short for advanced features. When you get stuck, you'll find yourself digging through forum posts and outdated examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Small Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CT.js has a fraction of the community that Phaser or Godot has. Finding help, tutorials, or plugins is significantly harder. For a beginner, this means more time stuck and less time building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Performance Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because CT.js tries to do everything, it can be slower and heavier than more focused engines. Larger projects may experience lag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The visual editor makes it seem easier than it is, but you'll still need to learn JavaScript. The mix of visual and code creates a confusing learning experience.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Beginners who want a visual editor but aren't afraid of code&lt;/li&gt;
&lt;li&gt;✅ Developers building small to medium 2D games&lt;/li&gt;
&lt;li&gt;✅ Beginners who want to export to desktop&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Absolute beginners with no coding experience&lt;/li&gt;
&lt;li&gt;❌ Developers building large, complex games&lt;/li&gt;
&lt;li&gt;❌ Anyone who just wants to code without a visual editor&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #6 — GDevelop
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 7/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; No-code game development&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;GDevelop is an open-source game engine that allows you to create games without writing code. It features a visual event system and supports both 2D and 3D game development.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;GDevelop is powerful for no-code development, but you'll hit a ceiling quickly. Complex game logic can be difficult or impossible to implement without code. The performance of games built with GDevelop can also be slower than code-based engines.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No code required&lt;/strong&gt; — Build games with visual events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free and open-source&lt;/strong&gt; — No licensing fees, no hidden costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual editor&lt;/strong&gt; — See your game come together in real-time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export options&lt;/strong&gt; — Web, mobile, and desktop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good for educators&lt;/strong&gt; — Great for teaching game design&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Limited Flexibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The visual event system is powerful but limited. Complex game logic can be difficult or impossible to implement without code. You'll hit a ceiling quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Performance Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Games built with GDevelop can be slower than code-based engines. This becomes noticeable with larger or more complex games.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Less Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You're limited to what the visual editor allows. You can't easily extend the engine or add custom features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Smaller Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GDevelop's community is growing but still small. Finding help, tutorials, or examples can be challenging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The visual event system is intuitive but still has a learning curve. Understanding how events work and connect takes time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Absolute beginners with no coding experience&lt;/li&gt;
&lt;li&gt;✅ Educators teaching game design&lt;/li&gt;
&lt;li&gt;✅ Quick prototyping and game jams&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Developers who want to learn coding&lt;/li&gt;
&lt;li&gt;❌ Developers building complex games&lt;/li&gt;
&lt;li&gt;❌ Anyone who needs full control&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #5 — Construct
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 7.5/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Professional no-code game development&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Construct is a visual game development tool with a drag-and-drop interface and powerful event system. It's one of the most popular no-code engines available.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;Construct is a powerful no-code tool, but it's not free. The free version is limited, and the full features require a paid license. For beginners, this can be a significant barrier. Additionally, because you're not writing code, you're not learning programming — which is a trade-off.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Powerful event system&lt;/strong&gt; — Complex logic without code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual editor&lt;/strong&gt; — Intuitive and well-designed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great documentation&lt;/strong&gt; — Well-maintained and comprehensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong community&lt;/strong&gt; — Large user base with many resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Professional features&lt;/strong&gt; — Export to multiple platforms&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Pricing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Construct is freemium. The free version is limited, and the full features require a paid license. For beginners, this can be a significant barrier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Limited Flexibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like GDevelop, you're limited to what the visual editor allows. Complex game logic can be difficult or impossible to implement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Performance Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Performance can be an issue for larger or more complex games. The abstraction layer adds overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Less Learning Value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because you're not writing code, you're not learning programming. This is a trade-off: you can build games faster but won't learn JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Proprietary Format&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your game files are in a proprietary format. If you ever want to switch engines, you'll have to start over.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Absolute beginners&lt;/li&gt;
&lt;li&gt;✅ Educators and students&lt;/li&gt;
&lt;li&gt;✅ Prototyping and game jams&lt;/li&gt;
&lt;li&gt;✅ Non-technical game designers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Developers who want to learn programming&lt;/li&gt;
&lt;li&gt;❌ Developers building complex games&lt;/li&gt;
&lt;li&gt;❌ Anyone who wants full control&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #4 — Kaboom.js
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 8/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Game jams and quick prototypes&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Kaboom.js is a lightweight JavaScript game library with a playful, modern API. It's designed for fast prototyping and game jams.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It Below Others?
&lt;/h3&gt;

&lt;p&gt;Kaboom.js is intentionally minimal. It lacks many features of a full engine: physics, particles, scene management, and audio are all limited. It doesn't scale well to larger projects. The simple API that makes it great for prototypes becomes limiting for complex games.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minimal boilerplate&lt;/strong&gt; — Get started with 5 lines of code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playful API&lt;/strong&gt; — Fun and intuitive to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No setup required&lt;/strong&gt; — Just include the CDN script&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good for learning&lt;/strong&gt; — Clear, readable code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great for game jams&lt;/strong&gt; — Fast results&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Limited Features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kaboom.js is intentionally minimal. It lacks many features of a full engine: physics, particles, scene management, and audio are all limited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Not for Large Projects&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kaboom.js doesn't scale well to larger projects. The simple API that makes it great for prototypes becomes limiting for complex games.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Smaller Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kaboom.js has a smaller community than Phaser. Finding help, tutorials, and examples is harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Less Documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The documentation covers the basics but is sparse for advanced features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Opinionated Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kaboom.js has a very specific way of doing things. This is great when it matches your style, but frustrating when you want to do something different.&lt;/p&gt;
&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Beginners learning core game logic&lt;/li&gt;
&lt;li&gt;✅ Game jam participants&lt;/li&gt;
&lt;li&gt;✅ Quick prototypes&lt;/li&gt;
&lt;li&gt;✅ Developers who like minimal APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Developers building large projects&lt;/li&gt;
&lt;li&gt;❌ Anyone who needs a full-featured engine&lt;/li&gt;
&lt;li&gt;❌ Developers building commercial games&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  #3 — Limn Engine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 9/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Absolute beginners who want to understand game engines&lt;/p&gt;
&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Limn Engine is a lightweight, zero-configuration 2D game engine built on HTML5 Canvas. It's designed for beginners who want to learn game development without fighting complex build tools.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Is It #3?
&lt;/h3&gt;

&lt;p&gt;Limn Engine is the best choice for &lt;strong&gt;learning how game engines work&lt;/strong&gt;. The zero-configuration setup means you can start coding immediately. The intuitive API is predictable and easy to remember. The four progressive skill levels give you a clear learning path. The dual-canvas rendering system is built for performance, even on low-end hardware. And it's completely free with no hidden costs.&lt;/p&gt;

&lt;p&gt;The only downsides are the smaller community (compared to Phaser) and the lack of a visual editor. But for a beginner who wants to learn, these are actually features — you learn more by writing code.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Zero Configuration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"epic.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Start coding!&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No npm. No build tools. No configuration. Just one file. Open it in a browser and start building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Intuitive API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The API is designed to be predictable and easy to remember:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// Keeps player on screen&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Screen shake&lt;/span&gt;
&lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;explosion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;particles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Explosion effect&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every function is designed to do exactly what you expect. You'll spend less time reading documentation and more time building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Clear Learning Path&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine is structured into four progressive skill levels:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What You Learn&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Beginner&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shapes, keys, collision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intermediate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Physics, camera, tilemaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Particles, scenes, effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10x Developer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Engine internals, performance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each level builds on the previous one. You'll always know what to learn next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Complete Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every feature has working examples that you can try immediately. The examples are simple, self-contained, and easy to understand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Built-in Performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dual-canvas rendering system (fake canvas + main canvas) is designed for performance, even on low-end hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Active Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine is actively maintained and improved. The developer is committed to making it better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. No Hidden Costs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine is completely free and open-source (MIT license). No paid features, no "pro" versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. No Visual Editor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is code-based. If you want a visual editor, look elsewhere. This is deliberate — Limn Engine is designed for learning through code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Smaller Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine has a smaller community than Phaser. This means fewer tutorials and fewer people to ask for help. However, the documentation is comprehensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Web Only&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Limn Engine is designed for the web. There's no native mobile or desktop export.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Limited Physics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The physics system is basic — gravity and collisions. If you need advanced physics, you'll need to build them yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Absolute beginners who want to learn game programming&lt;/li&gt;
&lt;li&gt;✅ Developers who prefer writing code over using visual tools&lt;/li&gt;
&lt;li&gt;✅ Students who want full control over their codebase&lt;/li&gt;
&lt;li&gt;✅ Anyone building a simple game or prototype&lt;/li&gt;
&lt;li&gt;✅ Developers who want a lightweight, fast engine&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Developers who want a visual editor&lt;/li&gt;
&lt;li&gt;❌ Developers building complex commercial games
· ❌ Anyone who needs native mobile/desktop export&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  #2 — Phaser
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 8.5/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Building complete 2D games quickly&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Phaser is one of the most popular JavaScript game frameworks for building 2D games. It includes everything you need: physics, input, animation, sound, and a scene system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Is It #2?
&lt;/h3&gt;

&lt;p&gt;Phaser is the most popular JavaScript game engine for a reason. It's feature-rich, well-documented, and has a massive community. You can build complete 2D games quickly without having to piece together different libraries. The ecosystem is huge — there are thousands of tutorials, examples, and plugins available.&lt;/p&gt;

&lt;p&gt;The downsides are the steeper learning curve and the complex scene system. But once you understand it, Phaser is incredibly powerful.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Feature-rich&lt;/strong&gt; — Everything you need is included&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large community&lt;/strong&gt; — Thousands of tutorials and examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good documentation&lt;/strong&gt; — Comprehensive and well-maintained&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-established&lt;/strong&gt; — Proven in many production games&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active development&lt;/strong&gt; — Regular updates and improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Steep Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Phaser has a lot of features, which means there's a lot to learn. The learning curve is steeper than simpler engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Complex Scenes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The scene system is powerful but complex. Understanding lifecycle methods and scene management takes time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Opinionated Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Phaser has a specific way of doing things. While this is helpful once you understand it, it can be frustrating when you want to do something different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Performance Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Performance can be an issue with many objects. While Phaser is fast, it's not as fast as more specialized rendering engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. File Size&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Phaser's bundle size is larger than simpler engines. While this is expected, it can be an issue for projects with strict size constraints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Beginners who want a complete engine&lt;/li&gt;
&lt;li&gt;✅ Developers building commercial 2D games&lt;/li&gt;
&lt;li&gt;✅ Anyone who wants a well-established ecosystem&lt;/li&gt;
&lt;li&gt;✅ Teams building larger projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Absolute beginners who want the simplest possible start&lt;/li&gt;
&lt;li&gt;❌ Developers building high-performance applications&lt;/li&gt;
&lt;li&gt;❌ Anyone who wants a minimal API&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  #1 — Godot (Web Build)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rating: 9.5/10&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best For:&lt;/strong&gt; Professional 2D game development&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Is
&lt;/h3&gt;

&lt;p&gt;Godot is a full-featured, open-source game engine with excellent 2D support. The Web build allows you to export games to HTML5. It's the only engine on this list that's truly professional-grade while still being beginner-friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Is It #1?
&lt;/h3&gt;

&lt;p&gt;Godot is the most complete engine on this list. It has everything you need: a powerful visual editor, great 2D support, a beginner-friendly scripting language, and export options for web, mobile, and desktop. It's free and open-source with no licensing fees.&lt;/p&gt;

&lt;p&gt;The learning curve is steeper than simpler engines, but once you understand the fundamentals, it becomes intuitive. It's the best choice for beginners who want to build professional games.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Everything included&lt;/strong&gt; — A complete, professional-grade engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual editor&lt;/strong&gt; — Powerful and intuitive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Great 2D support&lt;/strong&gt; — Built specifically for 2D games&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free and open-source&lt;/strong&gt; — No licensing fees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripting&lt;/strong&gt; — GDScript is designed for beginners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong community&lt;/strong&gt; — Growing rapidly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export options&lt;/strong&gt; — Web, mobile, desktop, and consoles&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Doesn't Work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Desktop-First&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The editor is a desktop application. You'll need to install it on your computer. The web version is only for exporting, not editing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Steep Initial Learning Curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Godot has more features than any other engine on this list. The initial learning curve is steeper than simpler engines. However, once you understand the fundamentals, it becomes intuitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. GDScript Syntax&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GDScript is beginner-friendly but different from JavaScript. If you plan to use JavaScript in other projects, learning a separate language might be a downside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Performance Can Be an Issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For highly complex or performance-critical projects, Godot may not be as fast as more specialized engines. However, for most 2D games, it's more than sufficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Smaller Community Than Unity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Godot's community is growing but still smaller than Unity's. However, there are many tutorials and resources available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who Should Use It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Beginners planning to build professional games&lt;/li&gt;
&lt;li&gt;✅ Developers who want a complete, full-featured engine&lt;/li&gt;
&lt;li&gt;✅ Anyone building 2D games for multiple platforms&lt;/li&gt;
&lt;li&gt;✅ Developers who want a free, open-source alternative to Unity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who Should Avoid It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Absolute beginners who want the simplest possible start&lt;/li&gt;
&lt;li&gt;❌ Developers who want to learn JavaScript specifically&lt;/li&gt;
&lt;li&gt;❌ Anyone who prefers a code-only approach&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📊 Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Learning Curve&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;th&gt;Visual Editor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Godot (Web Build)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9.5/10&lt;/td&gt;
&lt;td&gt;Professional 2D games&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phaser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8.5/10&lt;/td&gt;
&lt;td&gt;Complete 2D games&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limn Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9/10&lt;/td&gt;
&lt;td&gt;Learning fundamentals&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kaboom.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8/10&lt;/td&gt;
&lt;td&gt;Game jams &amp;amp; prototypes&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Construct&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7.5/10&lt;/td&gt;
&lt;td&gt;No-code development&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;Freemium&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GDevelop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;td&gt;No-code development&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CT.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6/10&lt;/td&gt;
&lt;td&gt;Visual editor + code&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;✅ Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MelonJS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.5/10&lt;/td&gt;
&lt;td&gt;Lightweight 2D games&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PlayCanvas (2D Mode)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.5/10&lt;/td&gt;
&lt;td&gt;Collaborative web-based 2D games&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pixi.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;7/10&lt;/td&gt;
&lt;td&gt;High-performance rendering&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"If you want to learn how game engines work, start with Limn Engine. If you want to build complete 2D games fast, choose Phaser. If you want to build professional games, choose Godot."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — one engine at a time.&lt;/em&gt; 🎮🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>discuss</category>
      <category>learning</category>
    </item>
    <item>
      <title>🧩 Understanding Components and Their Children in Limn Engine</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Sat, 25 Jul 2026 04:55:24 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/understanding-components-and-their-children-in-limn-engine-2643</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/understanding-components-and-their-children-in-limn-engine-2643</guid>
      <description>&lt;h1&gt;
  
  
  🧩 Understanding Components and Their Children in Limn Engine
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;A Complete Guide to Tile, Text, Sprite, AnimatedSprite, and Tctxt&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Component&lt;/code&gt; class is the foundation of every Limn Engine game. Everything you see on screen — players, enemies, coins, walls, text, sprites — is a Component or a child of Component.&lt;/p&gt;

&lt;p&gt;But Component isn't just one thing. It has several specialized children that handle specific tasks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Child Class&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Individual tiles in a tilemap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tctxt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Styled text with backgrounds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Animated sprites from sprite sheets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AnimatedSprite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple animation states&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Particle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Individual particles (used internally)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this guide, we'll cover all of them — what they are, when to use them, and how they work.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What We'll Cover
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is a Component?&lt;/li&gt;
&lt;li&gt;The Tile class — building blocks of levels&lt;/li&gt;
&lt;li&gt;The Tctxt class — styled text&lt;/li&gt;
&lt;li&gt;The Sprite class — animated characters&lt;/li&gt;
&lt;li&gt;The AnimatedSprite class — multiple animations&lt;/li&gt;
&lt;li&gt;The Particle class — visual effects&lt;/li&gt;
&lt;li&gt;Comparison table&lt;/li&gt;
&lt;li&gt;Common mistakes&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧩 Part 1: What Is a Component?
&lt;/h2&gt;

&lt;p&gt;A Component is the most basic building block in Limn Engine. Every visible object in your game is a Component.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Basics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A basic Component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5b8cff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;new Component(40, 40, "#5b8cff", 400, 300, "rect")&lt;/code&gt; — Creates a 40x40 blue rectangle at position (400, 300).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.add(player);&lt;/code&gt; — Adds it to the game world.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Properties
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x, y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;width, height&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Fill color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;speedX, speedY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Velocity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;angle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;physics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;Enable gravity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gravity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Gravity strength&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Component Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move based on speedX/speedY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;crashWith()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Collision detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clicked()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mouse click detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setImage()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setColor()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;destroy()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove from engine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Part 2: The Tile Class — Building Blocks of Levels
&lt;/h2&gt;

&lt;p&gt;Tile is a specialized Component used in tilemaps. Each tile represents a single cell in your level grid.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Tiles Work
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define a tile&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallTile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#654321&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// In a tilemap, tiles are created automatically&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1a3a1a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 1 = grass&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#2244aa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 2 = water&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#555555&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 3 = stone&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileFace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.tile&lt;/code&gt; — An array of tile templates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.map&lt;/code&gt; — A 2D array where each number refers to a tile template.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.tileMap();&lt;/code&gt; — Creates the tilemap object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.tileFace.show();&lt;/code&gt; — Renders all tiles to the fake canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tile vs Component
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tile&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General game objects&lt;/td&gt;
&lt;td&gt;Individual tiles in a tilemap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Creation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Automatic from a map&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Position&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Set manually&lt;/td&gt;
&lt;td&gt;Calculated from grid position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Players, enemies, items&lt;/td&gt;
&lt;td&gt;Walls, floors, decorations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Part 3: The Tctxt Class — Styled Text
&lt;/h2&gt;

&lt;p&gt;Tctxt is a specialized Component for displaying styled text with backgrounds and padding.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Basics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scoreText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Score: 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scoreText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;new Tctxt("24px", "Arial", "white", 20, 50)&lt;/code&gt; — Creates text at (20, 50) with 24px Arial white text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scoreText.setText("Score: 0");&lt;/code&gt; — Sets the text content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.add(scoreText);&lt;/code&gt; — Adds it to the game.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Full Constructor
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tctxt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;20px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// size&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// font family&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// color&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// x, y&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// align: "left", "center", "right"&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                  &lt;span class="c1"&gt;// stroke: false = fill, true = outline&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alphabetic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// baseline: "top", "middle", "bottom"&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rgba(0,0,0,0.5)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// background color (null = none)&lt;/span&gt;
    &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;                   &lt;span class="c1"&gt;// paddingX, paddingY&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tctxt vs Component Text
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Component (text)&lt;/th&gt;
&lt;th&gt;Tctxt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Padding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alignment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Limited&lt;/td&gt;
&lt;td&gt;✅ Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stroke&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Baseline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ease of use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Basic&lt;/td&gt;
&lt;td&gt;✅ Advanced&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Part 4: The Sprite Class — Animated Characters
&lt;/h2&gt;

&lt;p&gt;Sprite extends Component to handle animated sprite sheets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Basics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;new Sprite("hero.png", 64, 64, 4, 10, 400, 300)&lt;/code&gt; — Creates a sprite at (400, 300) from a sprite sheet with 4 frames, each 64x64, at speed 10.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How a Sprite Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Sprite Sheet: "hero.png"                                     │
│   ┌──────────┬──────────┬──────────┬──────────┐               │
│   │ Frame 0  │ Frame 1  │ Frame 2  │ Frame 3  │               │
│   │ 64x64    │ 64x64    │ 64x64    │ 64x64    │               │
│   └──────────┴──────────┴──────────┴──────────┘               │
│                                                                 │
│   Sprite draws each frame in sequence:                         │
│   Frame 0 → Frame 1 → Frame 2 → Frame 3 → Frame 0...          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sprite Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Advance to the next frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draw(ctx, x, y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Draw the current frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;play()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start/continue animation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stop()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pause animation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reset()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reset to frame 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sprite vs Component
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Sprite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Display&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A single color/image&lt;/td&gt;
&lt;td&gt;Multiple frames from a sprite sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Animation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frame control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Full control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple objects&lt;/td&gt;
&lt;td&gt;Characters, animated objects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Part 5: The AnimatedSprite Class — Multiple Animations
&lt;/h2&gt;

&lt;p&gt;AnimatedSprite extends Sprite to support multiple animation states.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Basics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AnimatedSprite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define animations&lt;/span&gt;
&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// frames 0-3, loops&lt;/span&gt;
&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// frames 4-11, loops&lt;/span&gt;
&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jump&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// frames 12-15, one-shot&lt;/span&gt;
&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;attack&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// frames 16-19, one-shot&lt;/span&gt;

&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMoving&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateAnimation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How Animations Work
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Sprite Sheet with multiple animations:                       │
│                                                                 │
│   ┌──────────┬──────────┬──────────┬──────────┬──────────┐   │
│   │ Idle 0   │ Idle 1   │ Idle 2   │ Idle 3   │ Run 0    │   │
│   ├──────────┼──────────┼──────────┼──────────┼──────────┤   │
│   │ Run 1    │ Run 2    │ Run 3    │ Run 4    │ Run 5    │   │
│   ├──────────┼──────────┼──────────┼──────────┼──────────┤   │
│   │ Run 6    │ Jump 0   │ Jump 1   │ Jump 2   │ Jump 3   │   │
│   ├──────────┼──────────┼──────────┼──────────┼──────────┤   │
│   │ Attack 0 │ Attack 1 │ Attack 2 │ Attack 3 │          │   │
│   └──────────┴──────────┴──────────┴──────────┴──────────┘   │
│                                                                 │
│   Animations:                                                   │
│   - idle: frames 0-3 (loops)                                   │
│   - run: frames 4-10 (loops)                                   │
│   - jump: frames 12-15 (one-shot)                              │
│   - attack: frames 16-19 (one-shot)                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AnimatedSprite Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addAnimation(name, start, end, speed, loop)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define an animation clip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;playAnimation(name)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch to a different animation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;updateAnimation()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Advance the frame counter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;faceLeft()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flip the sprite horizontally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;faceRight()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reset the flip&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;playAnimation&lt;/code&gt; Guard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;playAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ⚡ CRITICAL GUARD: Prevents resetting if already playing&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentAnim&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Only reset if the animation name changed&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentAnim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frameSpeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sprite vs AnimatedSprite
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Sprite&lt;/th&gt;
&lt;th&gt;AnimatedSprite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Animations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One continuous animation&lt;/td&gt;
&lt;td&gt;Multiple named clips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ None&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Switching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;td&gt;✅ &lt;code&gt;playAnimation()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple animations&lt;/td&gt;
&lt;td&gt;Characters with multiple states&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧩 Part 6: The Particle Class — Visual Effects
&lt;/h2&gt;

&lt;p&gt;Particle is a specialized Component used in the ParticleSystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Particles Work
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Particles are created by the ParticleSystem&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ParticleSystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Single particle&lt;/span&gt;
&lt;span class="nx"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ff6600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;life&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;gravity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Multiple particles&lt;/span&gt;
&lt;span class="nx"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;burst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#ff6600&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;life&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;randomSpeed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ps.emit()&lt;/code&gt; — Creates a single particle with custom options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ps.burst()&lt;/code&gt; — Creates multiple particles at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Particle Properties
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;life&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Frames until death&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gravity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downward acceleration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;friction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Speed multiplier per frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alpha&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starting opacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alphaFade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fade-out rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starting size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scaleFade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shrink rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📊 Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Parent&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Component&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Base class for all objects&lt;/td&gt;
&lt;td&gt;Players, enemies, items&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;Individual tiles in a tilemap&lt;/td&gt;
&lt;td&gt;Walls, floors, decorations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tctxt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;Styled text with backgrounds&lt;/td&gt;
&lt;td&gt;Score, menus, labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sprite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;Animated sprites from sprite sheets&lt;/td&gt;
&lt;td&gt;Characters, animated objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AnimatedSprite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sprite&lt;/td&gt;
&lt;td&gt;Multiple animation states&lt;/td&gt;
&lt;td&gt;Characters with idle/run/jump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Particle&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;Visual effects&lt;/td&gt;
&lt;td&gt;Explosions, sparkles, blood&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Common Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Why It's Wrong&lt;/th&gt;
&lt;th&gt;How to Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Using Component for text&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No background or alignment&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Tctxt&lt;/code&gt; instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Using Sprite for static images&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Overkill for one frame&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Component&lt;/code&gt; with &lt;code&gt;setImage()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Not calling &lt;code&gt;updateAnimation()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sprite doesn't animate&lt;/td&gt;
&lt;td&gt;Call it every frame&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forgetting the &lt;code&gt;playAnimation&lt;/code&gt; guard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Animation resets every frame&lt;/td&gt;
&lt;td&gt;It's built-in, don't override it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Component is the foundation — Tile builds levels, Tctxt displays text, Sprite animates characters, and AnimatedSprite manages multiple states."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — one component at a time.&lt;/em&gt; 🧩🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limn Engine Docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Reference&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/reference.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/reference.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Beginner Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/beginner.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/beginner.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>discuss</category>
      <category>programming</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title># 🎥 Mastering the Camera System — Part 2: The Fake Camera and When NOT to Sync</title>
      <dc:creator>Kehinde Owolabi</dc:creator>
      <pubDate>Thu, 23 Jul 2026 23:43:23 +0000</pubDate>
      <link>https://dev.to/kehinde_owolabi_e2e54567a/-mastering-the-camera-system-part-2-the-fake-camera-and-when-not-to-sync-j8j</link>
      <guid>https://dev.to/kehinde_owolabi_e2e54567a/-mastering-the-camera-system-part-2-the-fake-camera-and-when-not-to-sync-j8j</guid>
      <description>&lt;h1&gt;
  
  
  🎥 Mastering the Camera System — Part 2: The Fake Camera and When NOT to Sync
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Everything You Need to Know About the Fake Camera and Why Syncing Can Ruin Your Performance&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;In Part 1, we covered the basics of camera follow, shake, and zoom. But there's another camera in Limn Engine that most developers don't fully understand — the &lt;strong&gt;fake camera&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The fake camera is part of Limn Engine's dual-canvas rendering system. It controls the background layer (tilemaps, static decorations, and background images). Understanding when and how to use it is critical for performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most common mistake?&lt;/strong&gt; Syncing the fake camera with the main camera. In this article, we'll explain why that's a problem and how to fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What We'll Cover
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is the fake camera?&lt;/li&gt;
&lt;li&gt;How the dual-canvas system works&lt;/li&gt;
&lt;li&gt;The problem with syncing cameras&lt;/li&gt;
&lt;li&gt;When to sync (and when NOT to)&lt;/li&gt;
&lt;li&gt;The correct approach: resize, don't sync&lt;/li&gt;
&lt;li&gt;Practical examples&lt;/li&gt;
&lt;li&gt;Common mistakes and how to avoid them&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎮 What Is the Fake Camera?
&lt;/h2&gt;

&lt;p&gt;The fake camera is attached to the &lt;strong&gt;fake canvas&lt;/strong&gt; — an offscreen buffer that caches static content.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Canvas&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Camera&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Main canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamic content (player, enemies, particles)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;display.camera&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fake canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Static content (tilemaps, backgrounds, decorations)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fake.camera&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fake camera controls how the static background is rendered. It's part of the dual-canvas pipeline that gives Limn Engine its performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ How the Dual-Canvas System Works
&lt;/h2&gt;

&lt;p&gt;Before we dive into the fake camera, let's understand how the dual-canvas system works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    MAIN CANVAS                          │   │
│   │                                                         │   │
│   │   Dynamic content: player, enemies, particles          │   │
│   │   Camera: display.camera                               │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                    FAKE CANVAS                          │   │
│   │                    (offscreen)                         │   │
│   │                                                         │   │
│   │   Static content: tilemaps, backgrounds, decorations   │   │
│   │   Camera: fake.camera                                  │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   Both cameras are independent!                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;main canvas&lt;/strong&gt; is what you see on screen. It contains everything that moves — the player, enemies, bullets, and particles. Its camera is called &lt;code&gt;display.camera&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;fake canvas&lt;/strong&gt; is hidden. It's an offscreen buffer that contains everything that doesn't move — tilemaps, backgrounds, and static decorations. Its camera is called &lt;code&gt;fake.camera&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The two canvases are rendered separately and combined on screen. The fake canvas is drawn first (as the background), then the main canvas is drawn on top of it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each canvas has its &lt;strong&gt;own independent camera&lt;/strong&gt;. The fake camera is completely separate from the main camera.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The important thing to understand:&lt;/strong&gt; The fake canvas is designed to be drawn &lt;strong&gt;once&lt;/strong&gt; and then reused. Moving it every frame defeats its purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔴 The Problem: Syncing Cameras
&lt;/h2&gt;

&lt;p&gt;Many developers assume they need to sync the fake camera with the main camera. Let's look at the wrong way first.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ The Wrong Way: Syncing Cameras
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Step 1: Make the main camera follow the player&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Step 2: Copy the main camera's position to the fake camera&lt;/span&gt;
    &lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ❌ This is a mistake!&lt;/span&gt;
    &lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ❌ This is a mistake!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The main camera follows the player. This is correct.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We copy the main camera's position to the fake camera. This is &lt;strong&gt;the mistake&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now the fake camera moves with the main camera every frame.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why This Is a Problem
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;th&gt;Why It's Bad&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Defeats caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The fake canvas is supposed to be static. Moving it every frame breaks the cache.&lt;/td&gt;
&lt;td&gt;The fake canvas was designed to be drawn once and reused. Moving it forces it to be redrawn every frame.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance hit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instead of one draw call for the whole background, the engine has to re-render everything.&lt;/td&gt;
&lt;td&gt;The whole point of the fake canvas is to reduce draw calls. Syncing increases them back to normal levels.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wastes CPU&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The fake canvas was designed to be drawn once. Syncing forces it to be redrawn every frame.&lt;/td&gt;
&lt;td&gt;This defeats the performance benefits of the dual-canvas system.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No benefit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The fake canvas can be sized to the world, so moving it is unnecessary.&lt;/td&gt;
&lt;td&gt;There's a better way that doesn't hurt performance.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What You Should Do Instead
&lt;/h3&gt;

&lt;p&gt;Instead of moving the fake canvas, &lt;strong&gt;resize it to match the world size&lt;/strong&gt;. Let's look at the correct way.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ The Correct Approach: Resize, Don't Sync
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Set the World Size
&lt;/h3&gt;

&lt;p&gt;Here, you're going to define how big your game world is. This tells the engine the boundaries of your world so the camera knows how far it can move.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define how big your game world is&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this code does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.camera.worldWidth = 2000;&lt;/code&gt; — Sets the width of your game world to 2000 pixels. The camera will never show anything beyond this width.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.camera.worldHeight = 2000;&lt;/code&gt; — Sets the height of your game world to 2000 pixels. The camera will never show anything beyond this height.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; The world size determines how big the fake canvas needs to be. If you have a world that's 2000x2000, the fake canvas needs to be 2000x2000 to hold it all. If you don't set this, the camera defaults to 1000x1000, and anything outside that will be cut off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; At the very beginning of your game setup, before you render anything to the fake canvas.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Resize the Fake Canvas
&lt;/h3&gt;

&lt;p&gt;Now you're going to make the fake canvas exactly the size of your entire game world. This way, it can hold the whole map without needing to move.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Resize the fake canvas to match the world&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// Now 2000&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worldHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Now 2000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this code does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fake.canvas.width = display.camera.worldWidth;&lt;/code&gt; — Sets the fake canvas width to match your world width (2000 pixels).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fake.canvas.height = display.camera.worldHeight;&lt;/code&gt; — Sets the fake canvas height to match your world height (2000 pixels).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Because the fake canvas is now the full world size, it contains everything. The main camera just shows a window into it. The fake canvas never needs to move. If you don't do this, the fake canvas will be smaller than your world, and parts of your background will be cut off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; Right after setting &lt;code&gt;display.camera.worldWidth/Height&lt;/code&gt;, before rendering anything to the fake canvas.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Render Static Content Once
&lt;/h3&gt;

&lt;p&gt;Now you're going to draw all your static content — tilemaps, backgrounds, decorations — onto the fake canvas. This happens &lt;strong&gt;only once&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Render the tilemap to the fake canvas&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tileMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tileFace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add static decorations to the fake canvas&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decoration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this code does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.tileMap();&lt;/code&gt; — Creates the tilemap object that will hold your level data.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;display.tileFace.show();&lt;/code&gt; — Renders all the tiles onto the fake canvas. This draws your entire level.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fake.add(tree);&lt;/code&gt; — Adds a tree decoration to the fake canvas.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fake.add(decoration);&lt;/code&gt; — Adds another decoration to the fake canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; After this, the fake canvas contains a complete image of the entire static world. It never needs to be redrawn. If you don't do this, your static content will be drawn on the main canvas, which means it will be redrawn every frame — killing performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; After resizing the fake canvas, before the game loop starts.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 4: Update Only the Main Camera
&lt;/h3&gt;

&lt;p&gt;In your game loop, you only update the main camera. The fake canvas stays exactly where it is — it contains the entire world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Main camera follows the player&lt;/span&gt;
    &lt;span class="nx"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;follow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ✅ NO fake.camera sync needed!&lt;/span&gt;
    &lt;span class="c1"&gt;// The fake canvas is already the full world size&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this code does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;display.camera.follow(player, true);&lt;/code&gt; — Makes the main camera follow the player with smooth interpolation.&lt;/li&gt;
&lt;li&gt;No fake camera code is needed here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt; The main camera is like a window that moves over the fake canvas. The fake canvas itself never moves. If you sync the cameras, you'll defeat the caching and hurt performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use this:&lt;/strong&gt; Every frame in your update loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Visual Example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Wrong Approach (Syncing)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Frame 1: Move fake camera                                    │
│   Frame 2: Move fake camera                                    │
│   Frame 3: Move fake camera                                    │
│   ...                                                          │
│                                                                 │
│   Result: Fake canvas redrawn every frame.                   │
│           Caching is defeated.                                 │
│           Performance drops.                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Correct Approach (Resizing)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   Frame 1: Fake canvas contains entire world                  │
│   Frame 2: Main camera moves over it                          │
│   Frame 3: Main camera moves over it                          │
│   ...                                                          │
│                                                                 │
│   Result: Fake canvas drawn ONCE.                            │
│           Main camera shows a window into it.                 │
│           Performance is optimal.                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✅ When to Sync (and When NOT To)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Should You Sync?&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Static world&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Resize the fake canvas once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tilemap&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;The fake canvas can hold the whole map&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Background image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;One image, one draw call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decorations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;They don't move&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scrolling world&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Resize to world size, let the main camera scroll&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Camera shake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Shake the main camera only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallax layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Sometimes&lt;/td&gt;
&lt;td&gt;Only if you're implementing parallax manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic backgrounds&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚠️ Sometimes&lt;/td&gt;
&lt;td&gt;Only if the background actually changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🧠 Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What It Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fake canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Offscreen buffer for static content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fake camera&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Controls how the fake canvas is rendered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Syncing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copying &lt;code&gt;display.camera&lt;/code&gt; to &lt;code&gt;fake.camera&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Syncing defeats caching and hurts performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resize the fake canvas to match the world&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The rule&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Render static content once, move the main camera&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Common Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Why It's Wrong&lt;/th&gt;
&lt;th&gt;How to Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Syncing cameras&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Defeats caching and hurts performance&lt;/td&gt;
&lt;td&gt;Resize the fake canvas instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Moving the fake canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It should be static&lt;/td&gt;
&lt;td&gt;Set it once and leave it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redrawing the fake canvas&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wastes CPU cycles&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;display.once&lt;/code&gt; correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forgetting to set world size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fake canvas is too small&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;display.camera.worldWidth/Height&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🎯 The One-Line Summary
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"The fake camera is NOT for syncing — it's for caching. Resize the fake canvas to match your world, and let the main camera do the moving."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Draw your game into existence — with a camera system that performs.&lt;/em&gt; 🎥🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Resources
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 1: Follow, Shake, Zoom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/kehinde_owolabi_e2e54567a/mastering-the-camera-system-follow-shake-zoom-i41"&gt;dev.to/kehinde_owolabi_e2e54567a/mastering-the-camera-system-follow-shake-zoom-i41&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Limn Engine Docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10x Developer Guide&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://limn-engine-doc.vercel.app/10x.html" rel="noopener noreferrer"&gt;limn-engine-doc.vercel.app/10x.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>discuss</category>
      <category>intermediate</category>
    </item>
  </channel>
</rss>
