<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>Bart&#39;s small website | Arcade.ly | Blog</title>
  <subtitle>Writing, games &amp; side quests - Bart&#39;s small website</subtitle>
  <link href="https://arcade.ly/blog/atom.xml" rel="self" />
  <link href="https://arcade.ly/blog/" />
  <updated>2026-10-27T18:00:00Z</updated>
  <id>https://arcade.ly/blog/</id>
  <author>
    <name>Bart Read</name>
  </author>
  <entry>
    <title>arcade.ly now has an MCP server</title>
    <link href="https://arcade.ly/blog/2026/10/27/announcing-the-arcadely-mcp-server/" />
    <updated>2026-10-27T18:00:00Z</updated>
    <id>https://arcade.ly/blog/2026/10/27/announcing-the-arcadely-mcp-server/</id>
    <content type="html">&lt;p&gt;arcade.ly now has an &lt;a href=&quot;https://modelcontextprotocol.io/&quot;&gt;MCP&lt;/a&gt; server. If you use an AI assistant or agent that supports MCP you can point it at &lt;code&gt;https://arcade.ly/mcp&lt;/code&gt; and ask it about the site directly: which games there are, how to play them, who’s top of the hiscore tables, and what I’ve been writing about on here.&lt;/p&gt;
&lt;p&gt;It’s free, read-only, and there’s no sign-up or authentication required. Everything you need to connect is on &lt;a href=&quot;https://arcade.ly/mcp-server&quot;&gt;the MCP server page&lt;/a&gt;, which you’ll also find in the links at the bottom of every page.&lt;/p&gt;
&lt;h2 id=&quot;Why-bother?&quot;&gt;Why bother?&lt;/h2&gt;
&lt;p&gt;I mean, honestly, it’s just a bit of fun really. There is no serious value to having an MCP server for this site, but not everything needs to be serious.&lt;/p&gt;
&lt;p&gt;More and more people are finding things by using AI assistants and asking LLMs rather than searching, so I figured why not make it easy for them to find stuff on here. Whether anyone will actually use it or not, whether or not it’s useful for a small web personal site, is an entirely different matter.&lt;/p&gt;
&lt;p&gt;But I’m a tinkerer, I like to experiment with things and, ultimately, this is just a bit of fun. Just like my plan to create a gopherspace version of the arcade.ly site is as well.&lt;/p&gt;
&lt;h2 id=&quot;What-can-it-do?&quot;&gt;What can it do?&lt;/h2&gt;
&lt;p&gt;The server has four tools, all read-only:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list_games&lt;/code&gt; lists the games - currently &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; and &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt; - with what each is an homage to, where to play it, and its keyboard and touch controls.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_hiscores&lt;/code&gt; returns the best scores on a game’s hiscore table, for either its Modern or its Classic variant.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list_blog_posts&lt;/code&gt; lists the posts on this blog, newest first, optionally only those with a particular tag.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_blog_post&lt;/code&gt; returns a post in full, as Markdown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So you can ask things like “What’s the top score in Star Citadel?”, “How do I hyperspace in Shoot The Rocks?” or “What has Bart written about perceived performance?”, and get an answer that comes straight from the site, rather than from whatever a model happened to pick up in training.&lt;/p&gt;
&lt;p&gt;In Claude Code, for example, connecting takes one command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;claude mcp &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--transport&lt;/span&gt; http arcadely https://arcade.ly/mcp&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;Making-sure-it-can’t-slow-the-site-down&quot;&gt;Making sure it can’t slow the site down&lt;/h2&gt;
&lt;p&gt;My main concern has been ensuring the MCP server, or attempts to abuse it, don’t impact the rest of the site. In particular, nobody reading my blog or playing games should ever notice the MCP server exists. arcade.ly runs on a single, modest host, and the last thing I want is an enthusiastic agent somewhere hammering an endpoint while somebody’s trying to load Shoot The Rocks so they can beat their high score.&lt;/p&gt;
&lt;p&gt;So the server is built to cost as little as possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It answers everything from memory.&lt;/strong&gt; The site already keeps each game’s hiscore table in memory, so the MCP server reads those, and never asks the backing database for anything. The blog posts come from an index that’s generated every time the site is built, and loaded once when the server starts. None of the tool calls touch storage volumes or the database, although I do count them just to see if anyone is using the MCP (I DO NOT record any personally identifiable information or do any tracking though - it’s literally just counting them).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s stateless.&lt;/strong&gt; Every request gets a fresh server instance and a single JSON response. There are no sessions, no cookies, and no long-lived streams.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s rate limited twice:&lt;/strong&gt; 30 requests a minute from any one address, and a separate limit across all clients together, so even in the unlikely event of loads of clients connecting at once, and assuing they’re all behaving politely and sticking within the rate limit, they can’t add up to a problem in aggregate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also rebuild the content index every deploy, so the server never goes stale: new posts appear immediately.&lt;/p&gt;
&lt;h2 id=&quot;Not-just-MCP&quot;&gt;Not just MCP&lt;/h2&gt;
&lt;p&gt;While I was at it I made the rest of the site a bit friendlier to LLMs too:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There’s now an &lt;a href=&quot;https://arcade.ly/llms.txt&quot;&gt;llms.txt&lt;/a&gt;, following the &lt;a href=&quot;https://llmstxt.org/&quot;&gt;llms.txt convention&lt;/a&gt;: a plain summary of what the site is and where to find things. The blog has &lt;a href=&quot;https://arcade.ly/blog/llms.txt&quot;&gt;its own&lt;/a&gt;, listing every post, which is regenerated on every build. This isn’t a widely used standard at present so we’ll see where it goes: maybe it’ll get some traction, maybe it’ll die and I’ll remove support for it in future.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://arcade.ly/sitemap.xml&quot;&gt;sitemap&lt;/a&gt; is now regenerated on every build as well. This is something that I somehow broke some time ago when updating the build and CI processes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;robots.txt&lt;/code&gt; now names the AI crawlers explicitly, separating assistants that fetch a page because someone asked about it from crawlers that collect training data. I treat them the same at the moment, but that might change in future.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m somewhat ambivalent about what to do about the training crawlers as I haven’t fully understood whether, if the site content is in a model’s training data, that makes it more likely to be recommended by an assistant or not (and bearing in mind there’s no way to directly link something an LLM spits out due to its training back to its source). So for now I’m still allowing them to access the site but, if the load becomes too heavy, I might change that.&lt;/p&gt;
&lt;h2 id=&quot;Try-it&quot;&gt;Try it&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;https://arcade.ly/mcp&lt;/code&gt; to your assistant of choice, and ask it something. If something doesn’t work, or there’s something you’d like it to do that it doesn’t, please &lt;a href=&quot;https://arcade.ly/blog/Contact/&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Agentic Software Delivery &amp; The End Of Source Code As We Know It</title>
    <link href="https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/" />
    <updated>2026-09-25T12:43:17Z</updated>
    <id>https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/</id>
    <content type="html">&lt;p&gt;Last night I went out for drinks and pizza with colleagues. As we were chatting away one person said to the group that the logical conclusion of progress in agentic development, with direct human interaction with code ever decreasing, is the complete elimination of source code. Instead agents would work directly with binary machine-readable forms because, after all, if the code will never be read by humans, why would it need to be human-readable?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/agentic-delivery-abstraction-staircase.svg&quot; alt=&quot;A stack of seven layers headed &amp;quot;Which layer should agents author in?&amp;quot;. From top to bottom: specs, constraints and prompts; SQL, DSLs, config, markup and templating languages; Java, C#, Python, TypeScript, TSX, F# and Clojure; C, C++, Rust and Zig; JVM bytecode, .NET CIL, WebAssembly and LLVM IR, drawn dashed as a machine-oriented intermediate layer; assembly, noted as used for embedded systems, some kernel and driver code, boot code and hot loops; and machine code and binary, beside which is the question &amp;quot;Should agents operate here directly?&amp;quot;. A bracket marks the three layers from SQL down to C, C++, Rust and Zig as where most code was written before LLMs. An arrow runs down the left side of the stack, labelled &amp;quot;compiled, interpreted or JIT&#39;d downwards&amp;quot;.&quot;&gt;&lt;figcaption&gt;An imperfect, and in some aspects debatable, view of the hierarchy of abstraction layers at which programming languages operate. Much progress in programming languages over the decades has been at higher levels of abstraction that have helped software engineers become more productive. However, when the use case justifies it, we still use languages at lower levels of abstraction. So the question is, in the era of AI, LLMs, and agents, do we still need those higher levels or will agents work more effectively if they operate directly at the binary or machine code level?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I’ve heard similar things before and they’ve never sat quite right with me. But, for various reasons, I’ve never given them much serious thought. Last night was no exception: I was several drinks in, feeling relaxed and happy, and didn’t feel like getting into an argument that I couldn’t even articulate beyond a vague sense of unease with the position. And so the conversation quickly moved on.&lt;/p&gt;
&lt;p&gt;Today I’m sat on a train, heading home, for five hours. I’ve been re-reading The Mythical Man Month by Fred Brooks to pass the time. In doing so I’ve just encountered the reason for my unvoiced discomfort printed plain as day in the midst of chapter 16:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What does a high-level language accomplish? It frees a program from much of its accidental complexity. An abstract program consists of conceptual constructs: operations, datatypes, sequences, and communication. The concrete machine program is concerned with bits, registers, conditions, branches, channels, disks, and such. To the extent that the high-level language embodies the constructs wanted in the abstract program and avoids all lower ones, it eliminates a whole level of complexity that was never inherent in the program at all.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It’s that “whole level of complexity”: if we fail to eliminate it from the input to our agents, how is the cost counted?&lt;/p&gt;
&lt;p&gt;Unless there’s a compelling reason to, reverting to a lower level of abstraction from a higher level and taking on the complexity that goes with that - feeding all of that additional information into our agent - pollutes our reasoning space. It literally enlarges the state space that reasoning has to operate within. In doing so it increases the risk of hallucination and lowers the quality and relevance of generated output because all of the additional irrelevant information competes for attention with the details and concepts that are directly relevant to the problem we’re solving.&lt;/p&gt;
&lt;p&gt;For example, if I want an agent to change rules governing invoice approval for a certain class of supplier, what I want it to think about is approval rules, invariants, permissions, business rules and outcomes: I don’t want it to think about register allocation, memory layout, or the mechanics of marshalling and parsing HTTP requests and responses. The latter are all things that might need to happen to enable the former, but they’re not relevant to solving the business problem I have: to modify my invoice approval process to meet some new business requirement.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/agentic-delivery-find-the-rule.svg&quot; alt=&quot;The same invoice approval rule - class B suppliers need two approvers for invoices over £10,000 - shown three ways, with the lines that express the rule highlighted. Each version also handles invoice submission. As a declarative rule, a short submission action sits above the rule, and the six lines of the rule itself are highlighted. In Python, the rule is one highlighted function beneath an HTTP handler, so it seems like a really good fit for an abstraction level that&#39;s well matched to the problem domain. In x86-64 assembly, seven highlighted instructions are lost among around fifty lines of stack management, HTTP header and JSON parsing, and repository calls, none of which are directly relevant to the rule - there&#39;s too much extraneous detail.&quot;&gt;&lt;figcaption&gt;The same simplified invoice approval logic at three levels of abstraction, with the lines that actually express the rule highlighted. In this isolated example it seems like Python is a good match for the level of abstraction we need to work with our business rules, whereas the x86-64 code brings in all manner of extraneous detail relating to stack management and a bunch of other stuff that, whilst necessary, we don&#39;t care about so much for the problems we&#39;re considering.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If I force the agent to work at a lower level and ingest lots of extra unnecessary detail, that costs more in terms of reasoning, tokens (or their future equivalent), and time. It causes false starts, mistakes, and dead ends. Reasoning is degraded. Worse, it results in additional compaction cycles which themselves risk losing &lt;em&gt;relevant&lt;/em&gt; information, thus accelerating reasoning degradation. That degradation translates into both financial and compute costs that are simply wasted.&lt;/p&gt;
&lt;p&gt;As Fred Brooks pointed out in the quote above, abstracting to a higher level is beneficial because it constrains what needs to be considered. It removes all the unnecessary lower-level concepts and their details and instead allows us to focus on the core concepts and details of the problem we’re trying to solve.&lt;/p&gt;
&lt;p&gt;That’s just as relevant for LLMs and AI agents as it is for humans.&lt;/p&gt;
&lt;p&gt;Removing the lower-level detail cuts the complexity with which reasoning has to contend. That leads to better reasoning and higher-quality code generation. In an AI-SDLC&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/#fn1&quot; id=&quot;fnref1&quot; role=&quot;doc-noteref&quot; aria-label=&quot;Footnote 1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; that will contribute to improved downstream reasoning and interventions, which in turn will translate into improved quality in the delivered software.&lt;/p&gt;
&lt;p&gt;I’d argue that agentic development rewards abstraction and increases the value of patterns and practices that we’ve historically treated as aids to our own understanding and reasoning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;meaningful names&lt;/li&gt;
&lt;li&gt;modularity&lt;/li&gt;
&lt;li&gt;explicit contracts&lt;/li&gt;
&lt;li&gt;strong interfaces&lt;/li&gt;
&lt;li&gt;declarative representations&lt;/li&gt;
&lt;li&gt;separation of concerns&lt;/li&gt;
&lt;li&gt;local reasoning&lt;/li&gt;
&lt;li&gt;high-level languages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These constrain and communicate intent to whoever or whatever is working on our system, whether that’s a human being or an agent.&lt;/p&gt;
&lt;p&gt;AI doesn’t make software engineering abstractions obsolete. Instead, it shows they do more than simply overcome human limitations in understanding and cognition. Agents will be more productive using language representations and tooling at a level of abstraction that matches the problems and domains they’re working in. For many use cases that will be a higher level. This is very similar to the situation for humans.&lt;/p&gt;
&lt;p&gt;What might happen with time is that we – or perhaps AI agents under our direction – will develop new high-level languages, along with perhaps shorthand symbolic forms, that express intent in a way that works better with those agents. That I could certainly see.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Programming_language_generations#Fourth_generation_(4GL)&quot;&gt;4GL&lt;/a&gt; ideas have long since been incorporated into programming languages that are popular today, like Python and Ruby, and that will no doubt continue. What I think is more interesting is that the field has been opened up for not just the development but the day-to-day realisation of &lt;a href=&quot;https://en.wikipedia.org/wiki/Programming_language_generations#Fifth_generation_(5GL)&quot;&gt;5GL&lt;/a&gt; ideas. Arguably agentic development is itself a 5GL manifestation. And much of today’s discussion and research on AI-SDLC and AI governance touches on or incorporates 5GL ideas. For example, it’s not hard to see the connection between the concept of acceptance criteria and the idea of programming by constraint. Taking things further, we might start to see higher-level representations that don’t follow the typical linear, bloated, syntax-heavy, explicit nature of many programming languages so far developed for human use. Instead we might develop alternative high-level representations that are more suitable for ingestion, reasoning, and transformation by agents. These might be constraint-based, declarative, graph-like, relational, or specification-heavy.&lt;/p&gt;
&lt;p&gt;None of this precludes representations that are also human-readable&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/#fn2&quot; id=&quot;fnref2&quot; role=&quot;doc-noteref&quot; aria-label=&quot;Footnote 2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, or human-readable projections of those representations. But the point is that we might be able to move to higher-level representations of “code” than we’ve previously been able to make effective use of, and could see rapid progress and evolution in language development to enable more effective use by agents.&lt;/p&gt;
&lt;p&gt;Obviously this won’t impact all domains or scenarios equally: there will remain cases where lower-level languages still have relevance, such as in operating systems and embedded development. That will continue to be true regardless of whether humans or agents are writing the code.&lt;/p&gt;
&lt;p&gt;There are also plenty of reasons why human readability will remain an important facet of source code, especially in verticals like banking, healthcare, aviation, government, defence, and infrastructure. These are all heavily regulated, and systems developed within them are often highly consequential - incorrect operation can result in financial ruin, legal jeopardy, or sometimes even physical disaster.&lt;/p&gt;
&lt;p&gt;If humans and human-administered organisations are going to take responsibility for software and be held accountable for the consequences of its misbehaviour, then humans are going to need to be able to inspect its source representation (or a projection of it): to investigate, to understand, to audit, to govern, and to regulate.&lt;/p&gt;
&lt;p&gt;Nobody will accept “the agent gets it” as final assurance.&lt;/p&gt;
&lt;p&gt;Software also encodes organisational knowledge in its source code. Its version history, along with the latest snapshot, constitutes institutional memory. Software contains the results of countless decisions made about the business it operates in, the domain it models, and the wider world that it integrates with. For example, a customer &lt;em&gt;cannot&lt;/em&gt; transfer more than X between accounts in a single transaction without triggering &lt;a href=&quot;https://www.investopedia.com/terms/a/aml.asp&quot;&gt;AML&lt;/a&gt; procedures. Software is an executable specification that models and reflects some part of the reality in which it runs.&lt;/p&gt;
&lt;p&gt;We worry about loss of organisational knowledge, which can happen for many reasons: staff turnover, data loss (including loss of source code), changing supplier relationships, unforeseen disaster, or a combination of factors. Many organisations take steps to prevent such loss. Still, many of us have worked with organisations that &lt;em&gt;have&lt;/em&gt; lost knowledge of some part of their own systems and operations, and have helped them reconstitute that knowledge. At a very basic level, when I worked on .NET Reflector I helped several customers use it during source code reconstructions for systems where the original source code had been lost - and you’ll no doubt recognise that decompilation is only the first step on that journey. Full recovery of lost knowledge is challenging and expensive work. So there’s a strong argument against making institutional knowledge inaccessible by rendering it illegible to humans simply to benefit machines: I think we can create authoritative source representations that benefit both.&lt;/p&gt;
&lt;p&gt;However, we should avoid assuming that there’s a 1:1 correspondence between human readability and agentic tractability. It’s quite possible there will be agent-friendly representations that work incredibly well for the agents but are a real headache for us humans. In some situations it’s possible the benefits of tolerating those headaches will outweigh the costs. We’ll have to find ways to deal with that: perhaps with some form of projection, as I’ve already suggested, even if that’s not the canonical source of truth but “merely” a presentation layer for our benefit. Possibly that’s OK as long as we retain the ability for humans to inspect and understand the authoritative representation of the system.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/agentic-delivery-readability-tractability-2x2.svg&quot; alt=&quot;A two-by-two grid with human readability on the vertical axis and agentic tractability on the horizontal axis. High on both is the sweet spot: high-level languages, declarative specs, meaningful names and strong interfaces. Readable but hard for agents to act on: prose requirements, and intent buried in boilerplate. Low on both: hex dumps, obfuscated binaries and Brainfuck. Tractable for agents but hard for humans, drawn dashed because it doesn&#39;t exist yet: agent-native forms such as dense graphs and symbolic shorthand.&quot;&gt;&lt;figcaption&gt;Human readability and agentic tractability aren&#39;t necessarily the same thing, so it&#39;s not a given that what works well for humans will also work well for agents, and vice versa. But there is likely to be a sweet spot that offers benefits to both that make the trade-offs worthwhile. I suspect that sweet spot may not be so far removed from the kind of high level engineering artefacts we work with today.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I confess to a certain amount of scepticism here. I think we’ll prefer to avoid the machine-readable form as the source of truth because it could leave us having to reverse-engineer it if we find we can no longer trust that the human-readable projection represents the system accurately. I think it’s likelier that we’ll find more efficient ways to present high-level human-readable forms to agents, but that the human-readable form will remain authoritative. Tools such as compilers will continue to operate on the human-readable forms, thus preserving those forms as the source of truth.&lt;/p&gt;
&lt;p&gt;So my overall conclusion is this: as with human software development, agentic software development argues for operating at a level of abstraction that matches the problem domain. For most software delivery scenarios, this will be at a higher level, rather than a regression to the lower-level abstractions that we worked with in the past when we had little other choice. Moreover, the need for authoritative human-readable representations of systems and their behaviour remains as strong as ever.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
&lt;h2 class=&quot;section-divider footnotes-heading&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;a class=&quot;footnote-number&quot; href=&quot;https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/#fnref1&quot; role=&quot;doc-backlink&quot; aria-label=&quot;Back to reference 1&quot;&gt;1&lt;/a&gt;&lt;div class=&quot;footnote-body&quot;&gt;&lt;p&gt;AI-driven Software Development Lifecycle (sometimes AI-native or AI-powered Software Development Lifecycle, or other similar variants), abbreviated to AI-SDLC, is still an emerging and immature field - so much so that even the meaning of the acronym hasn’t fully coalesced yet.&lt;/p&gt;
&lt;p&gt;What this means is that whilst there are plenty of good resources available online, they tend to be on company websites and therefore push a version of AI-SDLC framed from the point of view of a particular organisation. They’re still valuable, but it’s obviously a good idea to draw from multiple sources. Here are a few that I’ve found useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://academy.claude.com/courses/ai-native-sdlc-playbook/introduction&quot;&gt;Claude Academy - AI-native SDLC handbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ibm.com/think/topics/ai-in-sdlc&quot;&gt;IBM - AI in the SDLC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://snyk.io/articles/complete-guide-ai-powered-software-development/&quot;&gt;Snyk - AI in SDLC: A Complete Guide to AI-Powered Software Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.port.io/blog/ai-sdlc&quot;&gt;Port - AI SDLC: A Practical Guide to SDLC AI Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/blogs/devops/ai-driven-development-life-cycle/&quot;&gt;AWS DevOps Blog - AI-Driven Development Life Cycle: Reimagining Software Engineering&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;a class=&quot;footnote-number&quot; href=&quot;https://arcade.ly/blog/2026/09/25/agentic-software-delivery-human-readable-code/#fnref2&quot; role=&quot;doc-backlink&quot; aria-label=&quot;Back to reference 2&quot;&gt;2&lt;/a&gt;&lt;div class=&quot;footnote-body&quot;&gt;&lt;p&gt;I’m perhaps being slightly loose in my terminology when I say “human-readable”. A human can read x86 or 6502 assembler (or even a stream of hex op codes if they’ve been doing it long enough); SQL is human-readable, JSON is human-readable, a dump of an AST is human-readable (relevant from when I worked on SQL Prompt), Brainfuck is technically human-readable. But they’re not all equally pleasant or easy to read or reason about. When I say human-readable, what I mean is that a human can understand it at an appropriate level of abstraction. Going back to our invoice approval example, we’ll find that code a lot easier to understand and reason about if it’s written in Python than we will if it’s written in x86 assembler.&lt;/p&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
  <entry>
    <title>Why is Agentic Coding so Tiring? An Incomplete Hypothesis</title>
    <link href="https://arcade.ly/blog/2026/08/29/why-is-agentic-coding-so-tiring/" />
    <updated>2026-08-29T19:09:00Z</updated>
    <id>https://arcade.ly/blog/2026/08/29/why-is-agentic-coding-so-tiring/</id>
    <content type="html">&lt;p&gt;When AI powered coding was in its infancy - a mere 4 years ago - with the first release of GitHub Copilot, I started to use it on a Python project or two, and I kind of found it to be a sort of Intellisense++. Despite the problems it was definitely a bit better than what had gone before, but perhaps not revolutionary.&lt;/p&gt;
&lt;p&gt;During the mania of 2023 I was leading development more than doing development, as Savanta looked to accelerate various elements of its platform and workflows with AI - from survey design and creation, fieldwork and data collection, and then through to analysis and delivery - and most of my use of LLMs was for activities other than coding.&lt;/p&gt;
&lt;p&gt;Fast forward to 2024 and I was working at a startup using a mix of the Claude Code agent in VS Code, and then Claude/ChatGPT chat sessions. We were also using CodeRabbit for code reviews. Honestly, it was all sort of OKish, but really not that great. But even then, I started to think maybe I understand how Welsh coal miners felt in Thatcher’s Britain in the early and mid-1980s. I felt like I was about to be made obsolete. I felt like it was the end of my career.&lt;/p&gt;
&lt;p&gt;In reality, due to reasons I’m not going to get into here, I was severely burned out and depressed. I took most of 2025 off as a result, and didn’t do much: some DIY, helping organise a local CTO group. At the end of the year I started a fractional CTO role with another startup, which I got really into for a few months from January 2026 to help get things off the ground.&lt;/p&gt;
&lt;p&gt;In the interim a lot had changed. AI agents had got good at coding. Not flawless, but really, really good. I remember the feeling the first time I got Claude to one-shot creation of a new feature, plus tests. It probably rolled in at 1500 lines of Ruby on Rails code, and it worked almost perfectly straight away. I found about 10 bugs, many of which were UX/interactivity issues, that needed to be fixed, but I was pretty happy.&lt;/p&gt;
&lt;p&gt;But the bigger and more important change was in me: I wasn’t burned out and uninterested any more. I was energetic, engaged, and eager to get my teeth into something. And Claude meant that I could create as fast as I could think of what I wanted to create, or so it felt. I’d still have in my mind a clear idea of the problem and how I wanted it to be solved, but I wouldn’t have to do all the typing in myself.&lt;/p&gt;
&lt;p&gt;And that was amazing. It really helped to reignite my passion and enjoyment of building software. I thought it was great, and I still think it’s great. But 8 months in I’ve had perhaps more time to reflect on the experience and, quite simply, had more experience.&lt;/p&gt;
&lt;p&gt;A lot of people have talked about how agentic development can be exhausting because, with a team of people, you can create far faster than you can understand everything you need to, especially when you think in terms of keeping your architecture clean and consistent so you can continue to move forward. It’s very easy to feel incredibly productive with agentic coding on an individual level whilst, at a team level, the combined effort is creating a giant mess of technical debt at extraordinary speed.&lt;/p&gt;
&lt;p&gt;There’s definitely something to that, and I’ve experienced it. It can feel like the real danger is with engineers who weren’t the best before agentic coding because, when you add that agent, the effect seems to be to magnify all their worst tendencies - like handing a loaded machine gun to a chimpanzee. But actually AI allows people to move so quickly that even amongst teams of very able engineers, slight misalignments can rapidly amplify. The biggest challenge in many softare organisations at the moment is probably scaling agentic developing effectively whilst avoiding those pitfalls.&lt;/p&gt;
&lt;p&gt;I think all this does credibly provide at least a partial picture that helps answer why agentic software development can feel weirdly exhausting, but I don’t think it’s the whole story.&lt;/p&gt;
&lt;p&gt;There are at least a couple of other reasons.&lt;/p&gt;
&lt;p&gt;The first is entirely prosaic: agentic development is still very new. Adoption is uneven and there are still plenty of engineers out there with little to no exposure because there are still plenty of organisations who haven’t adopted any level of AI as part of their software delivery practice. As with anything new, proficiency takes effort and effort is tiring, particularly when the field keeps evolving so rapidly, necessitating further effort to keep up with that evolution.&lt;/p&gt;
&lt;p&gt;The other reason I think is legitimately true, but I don’t know to what extent this is due to the way I personally work with coding agents versus other approachs, and therefore whether or not its a common experience.&lt;/p&gt;
&lt;p&gt;It boils down to this: I think I find agentic coding tiring because I still have to put in the effort to solve the problem in my mind, and I have to express that solution quite precisely in English (which can take anywhere from a few minutes to an hour depending on what I’m doing), but then I don’t get the sort of meditative flow state pay off of several hours or days of coding before I have to move onto the hard thinking that goes into solving the next problem. I.e., the frequency of those hard thinking sessions has increased significantly. And it also turns out that expressing requirements precisely, so that you get the results you really want, is quite a demanding and skilled discipline in its own right as well. Whereas, of course, whilst code that will execute and deliver the expected result needs to be very precise, a lot of that precision is repetitive, and the domain specific chunks of precision are spread throughout the code generated over a long session as opposed to a shorter, more focussed writing session.&lt;/p&gt;
&lt;p&gt;As I say, this might just be the way I use agents. I can believe that any pure vibe coder would read the above and be completely unable to identify with any shred of its sentiment.&lt;/p&gt;
&lt;p&gt;Maybe they’re right and I’m doing it all wrong. Who knows this early in the game?&lt;/p&gt;
&lt;p&gt;But for me the process is something like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Understand the problem and why it’s worth solving (and picking the right problems to solve is one of the most important acts in any software business that wants to succeed).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Figure out how to solve it, which might involve picking technologies, discussing different approaches with LLMs or humans (more the former for me in recent months), and deciding how to proceed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prompting and building a plan with the agent: expressing with fairly high precision the desired solution to the problem. Asking the agent to come up with an implementation plan, and iterating on that plan until I’m happy with it. This can sometimes take an hour or two, and involves reviewing all the changes it’s proposing to make.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Implementation: I just let the agent run in auto mode (because I’ve already reviewed its proposed changes), and have it build out the solution plus all tests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Testing, bug fixing: here I’m using the solution myself, making sure it works the way I want, looking for any flaws, bugs, deviations in expected behaviour, and - particularly - UX snafus that haven’t been caught by tests. Also, ensuring CI runs locally without any issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Push, open PR and code review: whilst Copilot is reviewing the code for me, I’ll also read through it myself and make sure all the changes make sense. This obviously is more challenging the bigger the PR. I find I can digest up to about 2,000 lines of code without too much difficulty, but beyond this I start to struggle and I’m more looking at structure of changes and where those changes are than reading every line in detail (here I’m relying on the LLM code review to spot any serious problems).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, once the review stage is complete, and CI passes, then we can merge. And in my world the ideal to aim for is always green CI on main === the software is ready to be deployed to production. (Note that I said ideal: making that a reality can be a long and demanding road in many situations.)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Very rarely will I say to an LLM: this is my problem, I want you to go and solve it, without having given some thought as to how I want it solved. Occasionally, when I’m tired, and/or when I’m being experimental or exploratory and there’s not too much riding on the outcome. If the outcome really matters then I tend to keep my agent on quite a short leash in terms of decision-making and really only use it as a way to do the typing out of the solution and tests, but very quickly.&lt;/p&gt;
&lt;p&gt;I’m glossing over a lot of details here as well: pre-commit hooks, pre-push hooks, any automated checks or scans you might perform as part of your pipeline (CI is doing a lot of heavy lifting here), traceability (a massive concern in regulated industries), security, and plenty else besides.&lt;/p&gt;
&lt;p&gt;I know these things are important but that’s not the point here. The point here is that losing the coding part of software development does, somewhat, rob us of the meditative period of typing in code that, in some ways, I always found somewhat restorative.&lt;/p&gt;
&lt;p&gt;Maybe it’s just a case of getting used to it. I suppose time will tell.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Thank You Joan Stark</title>
    <link href="https://arcade.ly/blog/2026/08/10/thank-you-joan-stark/" />
    <updated>2026-08-10T03:07:41Z</updated>
    <id>https://arcade.ly/blog/2026/08/10/thank-you-joan-stark/</id>
    <content type="html">&lt;p&gt;I wanted to take a brief moment to thank Joan G Stark and give her the credit she deserves for her contributions to the arcade.ly site header.&lt;/p&gt;
&lt;p&gt;Joan is a prolific ASCII artist who was very active in the early years of the web - from what I can find starting around 1996, and then up until roughly 2004. She’s been and remains hugely influential within the internet ASCII art community and is still popular to this day.&lt;/p&gt;
&lt;p&gt;Whilst her original website(s) are long since offline, you can still find her work at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20091028013825/http://www.geocities.com/SoHo/7373/&quot;&gt;https://web.archive.org/web/20091028013825/http://www.geocities.com/SoHo/7373/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://oldcompcz.github.io/jgs/joan_stark/&quot;&gt;https://oldcompcz.github.io/jgs/joan_stark/&lt;/a&gt; (repository at &lt;a href=&quot;https://github.com/oldcompcz/jgs&quot;&gt;https://github.com/oldcompcz/jgs&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are lots of different styles of ASCII art and, more generally, character art, and a ton of sites dedicated to ASCII and character art. These warrant a post in their own right, and maybe one day I’ll do that.&lt;/p&gt;
&lt;p&gt;But I wanted specifically to talk about Joan here because her style of ASCII art fits very well with the kind of aesthetic I am aiming for with this site.&lt;/p&gt;
&lt;p&gt;The basic arcade.ly logo hasn’t changed much since 2015 or 2017, whenever I first put the site online. I used an online text to ASCII art font generator to create the logo, which gave me this:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;                                 .___     .__
_____ _______   ____ _____     __| _/____ |  | ___.__.
&#92;__  &#92;&#92;_  __ &#92;_/ ___&#92;&#92;__  &#92;   / __ |/ __ &#92;|  |&amp;lt;   |  |
 / __ &#92;|  | &#92;/&#92;  &#92;___ / __ &#92;_/ /_/ &#92;  ___/|  |_&#92;___  |
(____  /__|    &#92;___  &amp;gt;____  /&#92;____ |&#92;___  &amp;gt;____/ ____|
     &#92;/            &#92;/     &#92;/      &#92;/    &#92;/     &#92;/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was pretty happy with this and added a blue to green gradient that was inspired by the blue/green reflective text in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Ultimate_Play_the_Game&quot;&gt;Ultimate Play the Game logo from the 1980s&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I did experiment with a pure ASCII art version of this logo on the website but I was getting frustrated with wrangling CSS, so in the end I grabbed an old Netflix template from GitHub and used that for a long time, along with a Hexo blog.&lt;/p&gt;
&lt;p&gt;But I was never really happy with either so recently I’ve spent some time retheming the site. It’s taken longer than I’d like to get it looking the way I want it to. Being a hacker who grew up in a certain era that means a sort of hyperreal neon-soaked 1980s terminal interface pastiche (obviously). It goes with the vector games I’ve so far built. But, also, Miami Vice remains one of my favourite TV shows. I’ve always loved the way that show was lit, and many of the interior designs (particularly after dark). Cowboy Bebop and Firefly are also influences. Anything that’s set in the future but a lot of the technology is kind of antique I really enjoy.&lt;/p&gt;
&lt;p&gt;I wanted to do more with the logo and header and I’d recently stumbled across Joan’s ASCII art gallery site via Parimal Satyal’s fun piece on &lt;a href=&quot;https://neustadt.fr/essays/the-small-web/&quot;&gt;Rediscovering the Small Web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Joan’s art style fits really well with the kind of neon vibe I’m going for here because she’s quite economical. She avoids dense character fills and in favour of emphasizing outlines and contours.&lt;/p&gt;
&lt;p&gt;She also mostly presented her work in white on black which is, (a) a great starting point given the background colours used here, and (b) makes it easy to experiment with adding my own colours to see how that looks.&lt;/p&gt;
&lt;p&gt;So I’ve used a couple of her beach scenes from https://web.archive.org/web/20010501170149/http://www.geocities.com/spunk1111/nature.htm#beach, and have adapted them slightly for use in my header.&lt;/p&gt;
&lt;p&gt;The first thing I wanted was some palm leaves or palm trees. I started down the leaves route, but I was struggling to either make or to find decent looking palm leaves that would fit in the space I had available. However, I really like this beach scene from Joan:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;           |
         &#92; _ /
       -= (_) =-
         /   &#92;         _&#92;/_
           |           //o&#92;  _&#92;/_
    _____ _ __ __ ____ _ | __/o&#92;&#92; _
  =-=-_-__=_-= _=_=-=_,-&#39;|&amp;quot;&#39;&amp;quot;&amp;quot;-|-,_
   =- _=-=- -_=-=_,-&amp;quot;          |
 jgs =- =- -=.--&amp;quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think the palm trees work well and I like the sand dune, so I wanted to keep both aspects. The water and the sun also look good, but I have a water effect - with reflections and ripples (Firefox compatible, no less!) - already, and the header scene is meant to be at night, so I removed both.&lt;/p&gt;
&lt;p&gt;This gave me:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;          _&#92;/_
          //o&#92;  _&#92;/_
            |   /o&#92;&#92; 
        _,-&#39;|&amp;quot;&#39;&amp;quot;&amp;quot;-|-,_
    _,-&amp;quot;          |
.--&amp;quot;        &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there I’ve made a couple more tweaks (added a bit more dune, added a couple more pieces of beach grass), and I’ve used that tweaked version, plus its reflection, to frame the main logo. I’ve also added colours, which I think work pretty well, and I’ve made the palm leaves occasionally wave a little bit in the breeze.&lt;/p&gt;
&lt;p&gt;Then I wanted some twinkling stars and maybe the moon, so I turned to another one of Joan’s beach scenes. In this case a night-time scene:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;            *      .         .                   .
    .                                      *         .         *
                      *            *           .-.
        *        *         .                  ( (       * 
                    .                   *      &#39;-&#39;
  ,&#92;//,.&#92;//&#92;/.         ,&#92;/,   ,&#92;/.//,                       .
  //o&#92;&#92;/o//o&#92;&#92; ,.,.,   //o&#92;   /o&#92;&#92;o&#92;&#92;         *     .      
    |   |  |  /###/#&#92;    |     |  |      .                 *
    |   |  |  |&#39; &#39;|:|    |`=.=&#39;|  |                      
jgs^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;&amp;quot;~~~~~~~~~~~~~~~~~~&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s a lot I like about this - the hammock is a particularly fun detail - but I just needed the moon and some stars so I trimmed down to a couple of chunks for the left- and right-hand sides of the header:&lt;/p&gt;
&lt;p&gt;Left:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;            *      .         .
    .
                      *
        *        * 
                    .&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;                   .
             *         .
     *           .-.
                ( (       * 
          *      &#39;-&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, I’ve made a couple of additional tweaks from there, repositioning some of the stars, adding colours, and implementing some twinkling, but this is very much a jgs creation.&lt;/p&gt;
&lt;p&gt;I also added a sort of dimming/sparking neon effect on mouse cursor that I’m 100% sure almost nobody will notice - but I like it - and that gave me my finished header.&lt;/p&gt;
&lt;p&gt;And now, having got it all working - and taking into account the reduced motion preference, where set - I “just” need to optimise the performance.&lt;/p&gt;
&lt;p&gt;Thanks again Joan!&lt;/p&gt;
&lt;h2 id=&quot;Postscript:-Where-is-Joan-now?&quot;&gt;Postscript: Where is Joan now?&lt;/h2&gt;
&lt;p&gt;I don’t know. Not really.&lt;/p&gt;
&lt;p&gt;Joan and her work were still &lt;a href=&quot;https://groups.google.com/g/alt.ascii-art/search?q=jgs&quot;&gt;referenced in the FAQ of the alt.ascii-art newsgroup until at least 2023&lt;/a&gt;. The 2023 date might simply coincide with when Google stopped archiving usenet posts, so I’d bet she’s still referenced in the FAQ today, in 2026.&lt;/p&gt;
&lt;p&gt;Back in 2013 somebody in the same group said that &lt;a href=&quot;https://groups.google.com/g/alt.ascii-art/c/a4crNRSw2wk?pli=1&quot;&gt;she still posted ASCII art from time to time&lt;/a&gt; - whether that was to her website, to the group, or both isn’t clear to me. With that being said, her most recent activity that I’ve been able to find &lt;a href=&quot;https://groups.google.com/g/alt.ascii-art/c/UILbqSaNQaM/m/luCVCeBSDjwJ&quot;&gt;dates from 2004&lt;/a&gt;, and I haven’t been able to find anything later than that.&lt;/p&gt;
&lt;p&gt;So it looks like she’s been mostly quiet online for more than 20 years. Both her &lt;a href=&quot;https://en.wikipedia.org/wiki/Joan_Stark&quot;&gt;Wikipedia article&lt;/a&gt; and her website talk about her children, who will all be grown by now, but there’s no more recent information.&lt;/p&gt;
&lt;p&gt;Sometimes people have enough of being online, and that’s fine. I like to think that Joan is living and enjoying her life, and I’m grateful that we all still get to enjoy her art.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Not many people are mourning for Stack Overflow</title>
    <link href="https://arcade.ly/blog/2026/07/23/not-many-people-mourn-Stackoverflow/" />
    <updated>2026-07-23T23:46:31Z</updated>
    <id>https://arcade.ly/blog/2026/07/23/not-many-people-mourn-Stackoverflow/</id>
    <content type="html">&lt;p&gt;The other day the topic of LLMs killing &lt;a href=&quot;https://stackoverflow.com/questions&quot;&gt;Stack Overflow&lt;/a&gt; came up again on &lt;a href=&quot;https://news.ycombinator.com/item?id=48956949&quot;&gt;Hacker News&lt;/a&gt;. Not for the first time, and &lt;a href=&quot;https://www.reddit.com/r/ChatGPT/comments/1gr66cr/rip_stackoverflow/&quot;&gt;not the only place it’s been discussed&lt;/a&gt; - it’s been talked about a lot in different forums and channels.&lt;/p&gt;
&lt;p&gt;And what’s notable about these discussions - given how beloved Stack Overflow used to be amongst software engineers - is the ambivalence, and sometimes the outright hostility of the conversations.&lt;/p&gt;
&lt;p&gt;And this obviously got me thinking about my own use and experience of Stack Overflow over the roughly 18 years of its existence, and how that changed over the period.&lt;/p&gt;
&lt;p&gt;So let me start at the beginning.&lt;/p&gt;
&lt;p&gt;It’s hard to overstate what a breath of fresh air Stack Overflow was when it arrived on the scene in late 2008. Prior to this support and knowledge sharing was spread across a variety of channels of varying quality and effectiveness:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Usenet: ubiquitous in the pre-web 1980s, and still popular through the 1990s, but increasingly overrun by spam and bots as the 2000s wore on. I barely touched it after about 2004.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mailing lists: still used today - for example, by Apache - and have remained popular within their niches. Lots of the archives have been lost over the years though.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;IRC: for decades the standard for interactive help and discussion, now supplanted by the likes of Slack and Discord&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Web forums: a real mixed bag from the likes of the paywalled (but easy to bypass) Experts Exchange, to vendor run sites like the MSDN forums, along with (then, at least) independent and community efforts like SQL Server Central, CodeGuru, DevShed, and the likes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blogs: engineers would write about the weird issues they’d run into, and how they’d fixed them, and people would sometimes discuss in the comments (although comment spam became a real issue as the 2000s wore on) - if you were lucky, your Google search would throw up one of these posts in its results and you’d find the answer you needed&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other sources of help included standard debugging and profiling tools, the documentation (the quality of MSDN and Java documentation 20+ years ago was such that very rarely did I need the online sources), bloody minded trial and error, and your colleagues and friends.&lt;/p&gt;
&lt;p&gt;Stack Overflow was different. I remember the launch marketing being very effective because, almost immediately it felt like, there was a strong community of smart, knowledgeable, and engaged developers on the site. It meant that even posts within the days after launch were getting plenty of answers and engagement.&lt;/p&gt;
&lt;p&gt;And then there was the usability: for a Q&amp;amp;A site, Stack Overflow trounced the other options. No scrolling through posts and flipping pages in a long running support forum discussion to get the nugget of information you needed. No trawling through endless newsgroup or email discussions. The answer would be just right there at the top, immediately below the question, marked as the answer by the person who asked it.&lt;/p&gt;
&lt;p&gt;And if you were submitting a question or a response it had a nice interface with simple to use formatting options.&lt;/p&gt;
&lt;p&gt;Overall the presentation was clean and uncluttered, and the gamification through points and voting kept quality and relevancy high.&lt;/p&gt;
&lt;p&gt;It felt like overnight it killed the support forum, particularly the &lt;a href=&quot;https://www.phpbb.com/&quot;&gt;phpBB&lt;/a&gt;/&lt;a href=&quot;https://www.vbulletin.com/&quot;&gt;vBulletin&lt;/a&gt; style support forum, at least for widely used technologies. I barely used anything other than Stack Overflow from the moment it was launched, and I remember that sinking feeling when occasionally I’d Google something and the results would force me towards MSDN - or wherever - rather than Stack Overflow.&lt;/p&gt;
&lt;p&gt;For the first few years it was genuinely great.&lt;/p&gt;
&lt;p&gt;I guess I noticed things starting to change around 2013 or 2014, and the darker and more dysfunctional side of the gamification, as well as its more prosaic limitations, started to show.&lt;/p&gt;
&lt;p&gt;I first noticed what was basically karma farming activity by a portion of the sites users. I don’t know how widespread this was but one factor that stoked this was some employers started looking at Stack Overflow scores as part of assessing job applicants for interview. I mean, great, if you want to employ someone who’s going to spend more time on garnering internet points than they’re going to spend on working for you, I suppose. But, also (and bluntly), screw you for amplifying the negative effects of gamification on Stack Overflow and helping to make it an overall less attractive place to contribute, and a less useful site to visit.&lt;/p&gt;
&lt;p&gt;But then I also increasingly ran across questions where the accepted answer either wasn’t the best answer amongst those that had been submitted (e.g., didn’t generalise well, not future proof, fragile/hacky, etc.), or had been at one time correct but the evolution of technology had rendered it out of date and no longer best practice, or sometimes just flat wrong.&lt;/p&gt;
&lt;p&gt;As time wore on the concentration of mod powers amongst a relatively small cadre of users, who’d amassed tens or hundreds of thousands of points, led to an elitism that made the site incredibly unwelcoming for newcomers. It engendered something that felt like a throwback to the RTFM culture of software engineering and IT from the 1980s and 1990s.&lt;/p&gt;
&lt;p&gt;You got issues like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;superficially similar questions being closed as duplicates despite the presence of key differences that warranted separate treatment,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;legitimate questions that were closed for being too subjective or opinion-based, too broad, not reproducible, even though site users wanted to discuss them,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;pedantry, bikeshedding, and nitpicking - along with fighting/flaming in the comments - over often superficial issues that didn’t make a valuable contribution to the technical content of the site, or the help people received from it,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;inconsistent moderation as how your question was treated depended on which “powerful” user(s) spotted it first.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.blog/2010/10/04/asking-better-questions/&quot;&gt;Blogs like this&lt;/a&gt; - a relatively early piece from Stack Overflow themselves - whilst well intentioned and meant to mitigate the excesses of low effort posting, certainly contributed to the situation.&lt;/p&gt;
&lt;p&gt;Here, for example, is a key quote from that post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We believe &lt;strong&gt;asking questions on our site is a privilege, not a right&lt;/strong&gt;. If, after a few fair attempts, you haven’t been able to prove that your contributions to Stack Overflow make it at least … not-worse … then we reserve the right to refuse your questions. If we don’t do our part to cull the bad questions, then we risk alienating the true experts who provide what really matters: the answers!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Emphasis theirs, not mine!)&lt;/p&gt;
&lt;p&gt;So the site became increasingly frustrating to use. It felt like it existed more to curate content than to help people, which is weird for a Q&amp;amp;A site. It was still useful: I found myself happy enough to refer to it, despite having to do more digging and vetting of answers. But I also found myself not wanting to actively contribute because it wasn’t worth the hassle and negativity.&lt;/p&gt;
&lt;p&gt;Here’s a story from 2015 - I think the last time I posted - that shows how toxic and frustrating the site had become to contribute to…&lt;/p&gt;
&lt;p&gt;I’d always been someone who found more answers on Stack Overflow than I ever posted. Even more rarely I’d ask a question. But, when I did, I’d always try and follow the spirit of the old netiquette rules of yore, do my research first, and think in terms of, if I was a support engineer trying to help with this, what would I need to know?&lt;/p&gt;
&lt;p&gt;Because I have supported products, and we’ve all seen enough bug reports and questions come in where we can straight away tell when someone is going to be easy to help - even if they have a particularly tricky problem - versus someone who’s going to prove more challenging to help.&lt;/p&gt;
&lt;p&gt;So I had this question about Elasticsearch. I forget exactly what it was: I think it was something to do with sort order and pagination, and we had some fairly gnarly requirements on how to prioritise matches and fields in our search results. This was at a time when the Elasticsearch documentation wasn’t great, and in fact you were actively encouraged to post on Stack Overflow and tag your question to get help if you couldn’t find an answer to your question.&lt;/p&gt;
&lt;p&gt;So I wrote out in detail what I’d done, where I’d got stuck, what I’d read and tried to get unstuck, etc. I put in code samples, and queries - all this sort of stuff. It probably took me 30 minutes or more to pull all the information I had together into a coherent post. Then I spent some time checking it over pretty thoroughly and editing for clarity before I submitted.&lt;/p&gt;
&lt;p&gt;So what do you think happened?&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;very first comment&lt;/em&gt; I received was from some insufferable prick saying words to the effect of, “Oh, so you want us to do your work for you, are you going to pay us too?”&lt;/p&gt;
&lt;p&gt;Literally, WTF dude? Why even post that? You might not want to help, and that’s fine, but if that’s the case the option to simply move on and go about your day without getting involved is always available. Especially because I had done exactly what I’d been instructed to do by Elasticsearch, and I’d followed the guidance for posting questions from Stack Overflow, and this kind of snippy smartarse no value response is what I get?&lt;/p&gt;
&lt;p&gt;As you can probably tell, it still irritates me today (11 years later!), although I think at the time I managed to respond with relatively good grace.&lt;/p&gt;
&lt;p&gt;Anyway, I don’t remember what the specific outcome was, other than I didn’t get a sufficiently satisfactory answer from Stack Overflow. As a result, lacking other channels or forums to find help, nor having anyone else in the organisation I was working for - or friends, former colleagues, or associates - with any knowledge of Elasticsearch, and lacking a clear picture from the documentation… I spent roughly a day layering some godawful and somewhat baroque hack on top of Elasticsearch to get the results into the shape we needed. It wasn’t the “right” way to solve the problem: I should have been able to get what I wanted using the Elasticsearch’s query syntax. But I knew it would work (and it did), and I had other high priority work I needed to move onto. The business concerned has continued to be very successful since then so, for all I know, that hack is still the working, in production solution for that search function (because I don’t think anyone would be particularly keen to roll up their sleeves and get any more deeply involved unless they really had to).&lt;/p&gt;
&lt;p&gt;So, as I say, I think that might have been the last question I posted on Stack Overflow, and it might be my last contribution to Stack Overflow full stop.&lt;/p&gt;
&lt;p&gt;Over the years, because of the issues above, I simply started finding Stack Overflow less and less useful. I’d find I was getting information from other sources: GitHub issues particularly, but even occasionally sites like Reddit. And then of course more and more projects started using Discord for support and discussion. I’ve never really been a big fan of Discord for this because I don’t like walled gardens and, whilst Stack Overflow is a proprietary platform, all of the information on that platform is public, indexed by search engines, and now (of course) in the training datasets of every frontier LLM provider.&lt;/p&gt;
&lt;p&gt;Stack Overflow launched so well, and worked so well for its first few years. As I said, it was absolutely a breath of fresh air. But as time went on it felt like Stack Overflow thought their model was the last word in online Q&amp;amp;A and they didn’t want to evolve to address the flaws in their model. Even just making the site a bit more friendly and positive, particularly towards newcomers, would have gone a long way.&lt;/p&gt;
&lt;p&gt;Would that have changed the outcome in the face of AI?&lt;/p&gt;
&lt;p&gt;Perhaps. Perhaps not. But I wonder if, without all that frustration and resentment that had built up over the years in the community that were at least nominally Stack Overflow’s target audience, whether the collapse would have been so complete. If you look at &lt;a href=&quot;https://data.stackexchange.com/stackoverflow/query/1953768/stackoverflow#graph&quot;&gt;the graph of new questions per day on Stack Overflow&lt;/a&gt; you can see that from the beginning of 2023 AI simply accelerated a downward trend that had begun maybe even as early as 2016 or 2017. And, even without that AI acceleration, if you draw a trendline along the prior decline, it looks like Stack Overflow would have been dead by 2030 or maybe 2031. That’s what makes me think there’s more than just AI at play: the rot, in terms of the attitude and cultural issues, had become entrenched 10 years ago to a point where it started to hurt that site.&lt;/p&gt;
&lt;p&gt;In fact the site peaked in 2014, which is weird when you consider that between 2014 and now, there’s been huge growth in the number of software engineers globally (something like 2.5x growth between 2014 and 2026 according to Google), huge growth in the variety and sheer number of technologies we use (some maturation of course), including lots of new technologies, and then also massive evolution of even mature technologies. So there’s lots of questions still to be asked.&lt;/p&gt;
&lt;p&gt;Stack Overflow is now - and the more I look at that graph the more I think it perhaps has been for a long time - a site that, like the dinosaur in A Sound of Thunder, is dead but maybe hasn’t realised it yet. It does still get a few hundred questions rolling in each day, but that’s less than 1% of its peak between 2014 and 2017.&lt;/p&gt;
&lt;p&gt;So do I feel about Stack Overflow?&lt;/p&gt;
&lt;p&gt;Well, as much as I’m in some ways sad to say this, there’s a part of me that sort of feels like good riddance, actually. They kind of did it to themselves. It feels a bit like just deserts.&lt;/p&gt;
&lt;p&gt;OK. So that’s true, but it’s not the whole truth - it’s not the way I feel in the round. It’s me thumbing my nose at the vanity of the haughtily arrogant and high-handed mods who abused their fleeting powers for a time.&lt;/p&gt;
&lt;p&gt;On the more positive side, Stack Overflow had a good run, they were very successful for a good long while (which is something many technology businesses never achieve at all, never mind sustain for year after year), and I know that the people who founded the site deservedly did very well from it - and I hope (although I don’t know) that the early employees similarly benefitted. Critically, they also - objectively - help a lot of people, probably millions of engineers all told have benefitted from what Stack Overflow built and what Stack Overflow was. And Stack Overflow continued to help them for years and years. I used it almost every day at work for probably 10 years, and was still a pretty regular user for another 3 or 4 years on top of that.&lt;/p&gt;
&lt;p&gt;All good things come to an end. Everything we build as human beings will one day disappear. Most of it will disappear within our own lifetimes. Relatively few of us will create anything that outlasts us, except for our children. And, honestly, we need to stop feeling bad about that because it is the very nature of our existence. More than that: it is the very nature of the universe within which we exist.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Today I learned: destructuring assignment in JavaScript</title>
    <link href="https://arcade.ly/blog/2020/05/01/today-i-learned-destructuring-assignment/" />
    <updated>2020-05-01T11:57:31Z</updated>
    <id>https://arcade.ly/blog/2020/05/01/today-i-learned-destructuring-assignment/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Today I learned a really nice piece of syntactic sugar from ECMAScript 2015 (formerly known as ES6) that I don’t believe I’ve made use of before: destructuring assignment. This breaks up a complex structure into simpler component parts. It can be used as a shorthand way of breaking up an objects members into a set of local variables that can increase legibility of your code. It also allows you to assign default values for parts that might be missing from the original structure.&lt;/em&gt;&lt;/p&gt;
&lt;section id=&quot;blogPostNavigation&quot;&gt;
&lt;h2 id=&quot;Contents&quot;&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2020/05/01/today-i-learned-destructuring-assignment/#Contents&quot;&gt;Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2020/05/01/today-i-learned-destructuring-assignment/#Background&quot;&gt;Background&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2020/05/01/today-i-learned-destructuring-assignment/#Destructuring-assignment-example&quot;&gt;Destructuring assignment example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;h2 id=&quot;Background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;For arcade.ly I don’t tend to make much use of libraries on the client side. This is because, as I’ve said elsewhere, when I started building &lt;a href=&quot;https://arcade.ly/&quot;&gt;these arcade games&lt;/a&gt; I wanted to see how far I could get using only the APIs built directly in to web browsers. I wanted to become more familiar with the capabilities built directly into browsers.&lt;/p&gt;
&lt;p&gt;The server is a different story: I’ve made significant use of libraries because it’s tricky to build a Node back-end of any substance without them. Also, who really wants to write services for HTTP2 support, logging, and database access themselves?&lt;/p&gt;
&lt;p&gt;I’ve also allowed myself to slip a bit when it comes to pages that aren’t hosting games: bootstrap’s snuck in there, I’m using the &lt;a href=&quot;https://github.com/klugjo/hexo-theme-alpha-dust&quot;&gt;AlphaDust theme&lt;/a&gt; for &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt; - although the blog itself is all precompiled to static HTML - and so on. For me these aren’t the fun bits to build so I defer to the excellent work of others.&lt;/p&gt;
&lt;p&gt;When it comes to &lt;a href=&quot;https://arcade.ly/&quot;&gt;the games&lt;/a&gt; I only use &lt;a href=&quot;https://lodash.com/&quot;&gt;lodash&lt;/a&gt;, plus &lt;a href=&quot;https://beyonk.com/uk&quot;&gt;Beyonk’s&lt;/a&gt; excellent &lt;a href=&quot;https://github.com/beyonk-adventures/gdpr-cookie-consent-banner&quot;&gt;gdpr-cookie-consent-banner&lt;/a&gt;. For the latter I just wanted something that would work well across all pages and, because it’s kind of fiddly and for me not that interesting, I was happy enough to use something built by others. I’ve made a few tweaks to the behaviour, but nothing major, and just load it in as a separate bundle at the very bottom of every page so it doesn’t slow down rendering.&lt;/p&gt;
&lt;p&gt;Recently, after a friend reported a bug that I couldn’t reproduce relating to this library, I added a global error handler that simply fires error messages, line numbers, and source files wrapped up in events to Google Analytics.&lt;/p&gt;
&lt;p&gt;What I discovered is that I had been a bit unwary in pulling the library in without looking at it in a bit more detail because its use of ES6 (and later) JavaScript syntax wasn’t playing well with Internet Explorer 11. Unbelievably IE11 still accounts for anywhere up to 2% of site users who actually play games and, whilst many of the errors wouldn’t stop them, there’s been the odd one that would, which I’ve wanted to fix. Plus, of course, I want to ensure the GDPR consent banner appears for everyone (so far I’m not bothering to hide it automatically for people outside the EU, or not covered by CCPA), and that cookies are allowed/denied accordingly.&lt;/p&gt;
&lt;h2 id=&quot;Destructuring-assignment-example&quot;&gt;Destructuring assignment example&lt;/h2&gt;
&lt;p&gt;IE11 was pretty unhappy about this line of code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; duration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; easing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; identity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;tick&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; tick$$1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; noop&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; css &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And whilst IE11 does support &lt;em&gt;some&lt;/em&gt; of ES2015 (see https://caniuse.com/#feat=es6), apparently destructuring assignment isn’t covered. Easy enough to fix.&lt;/p&gt;
&lt;p&gt;Getting back to the code, what does the above line actually do? Quite a bit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It assigns a number of local variables based on members of &lt;code&gt;config&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It drills in to one of those members (&lt;code&gt;tick&lt;/code&gt;) and assigns the value of its &lt;code&gt;tick$$1&lt;/code&gt; field to a local variable (also called &lt;code&gt;tick$$1&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It assigns default values to all local variables except &lt;code&gt;css&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Defaults can be any type of value: &lt;code&gt;delay&lt;/code&gt; and &lt;code&gt;duration&lt;/code&gt; are set to integers; &lt;code&gt;easing&lt;/code&gt; and &lt;code&gt;tick$$1&lt;/code&gt; are set to functions&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This short line of code is therefore equivalent to the much more verbose, and much harder to read:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; delay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delay &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    duration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    easing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;easing &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; identity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    tick$$1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tick &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tick&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tick$$1 &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; noop&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; noop&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    css &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;css&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I know which I prefer, but IE11 doesn’t agree with me unfortunately.&lt;/p&gt;
&lt;p&gt;Some obvious and not so obvious points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We haven’t defined &lt;code&gt;identity&lt;/code&gt; and &lt;code&gt;noop&lt;/code&gt;. For our purposes their values aren’t really important but, for the sake of completeness, I’ve provided definitions below&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Note that the local variable names generally need to match the names of fields/members in the object being destructured: any variables that don’t will just be set to &lt;code&gt;undefined&lt;/code&gt; unless defaults are specified (this behaviour can prove very useful though!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It’s also possible to destructure the values in an array: see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment for an example&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There’s absolutely no requirement to destructure all fields in an object or all members of an array: you can take as many or as few of them as you like&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s an example showing what our &lt;code&gt;config&lt;/code&gt; object might look like to make the situation clearer:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token function-variable function&quot;&gt;easing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;gimmeSomeEase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;thingummy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; aResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Maybe do some stuff here&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; aResult&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;tick&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function-variable function&quot;&gt;tick$$1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Do some other stuff here&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foreground-color: #AAAAAA&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, as promised, here are the definitions for &lt;code&gt;identity&lt;/code&gt; and &lt;code&gt;noop&lt;/code&gt; functions, which we’re using as default values for &lt;code&gt;easing&lt;/code&gt; and &lt;code&gt;tick$$1&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;noop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;identity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, destructuring assignments are a powerful technique for reducing bloat and making code more readable: enjoy using them!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>JavaScript: Microbenchmarks and their pitfalls - trig, random numbers, sqrt, and lookups</title>
    <link href="https://arcade.ly/blog/2020/04/02/javascript-pitfalls-of-microbenchmarks-trig-functions-math-random-vs-lookups/" />
    <updated>2020-04-02T09:50:43Z</updated>
    <id>https://arcade.ly/blog/2020/04/02/javascript-pitfalls-of-microbenchmarks-trig-functions-math-random-vs-lookups/</id>
    <content type="html">&lt;!-- TODO: BEFORE YOU PUBLISH THIS MAKE SURE YOU GET YOUR KEYWORDS IN!!! --&gt;
&lt;p&gt;&lt;em&gt;In the midst of all the depressing news about coronavirus what better way to cheer everybody up than with a post about JavaScript microbenchmarks. That’ll never start a flamewar, will it? But whilst I was doing some work on arcade.ly recently I thought it was worth revisiting my decision to go with lookup tables for trig functions and the like, and see if perhaps I shouldn’t instead be using functions provided by JavaScript’s global &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math&quot;&gt;Math&lt;/a&gt; object instead.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before we get into it, let’s get this out of the way: I know microbenchmarks are fraught with pitfalls. It’s super-easy to write a microbenchmark, particularly if a tight loop is involved, that any half-competent compiler or JIT will be able to optimise away to pretty much nothing, and thereby end up with incredibly skewed results.&lt;/p&gt;
&lt;p&gt;Other people know this as well. Here’s a comment &lt;a href=&quot;https://news.ycombinator.com/item?id=22715947&quot;&gt;on a recent Hacker News discussion about an article that claimed &amp;quot;JITs are un-ergonomic&amp;quot;&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/HackerNews-JITsAreUnergonomicComment.png&quot; alt=&quot;Comment on &#39;JITs Are Unergonomic&#39; discussing the looping pattern common in JavaScript microbenchmarks&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Absolutely spot on: as &lt;a href=&quot;https://news.ycombinator.com/user?id=pizlonator&quot;&gt;pizlonator&lt;/a&gt; says, the antipattern is real. A few years back I read a blog post - that I wish I could still find find - that covers a similar topic as it relates to C++.&lt;/p&gt;
&lt;p&gt;So why am I bothering to write this?&lt;/p&gt;
&lt;p&gt;Well, because I suppose my position is that as with many things in software development and life, it’s not so simple.&lt;/p&gt;
&lt;p&gt;A few years ago, when I first started working on &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt;, I made the decision to use lookup tables for common trig functions (sine, cosine, tangent), along with square roots, rather than use the built-in &lt;code&gt;Math.sin()&lt;/code&gt;, &lt;code&gt;Math.cos()&lt;/code&gt;, &lt;code&gt;Math.tan()&lt;/code&gt; and &lt;code&gt;Math.sqrt()&lt;/code&gt; functions.&lt;/p&gt;
&lt;blockquote&gt;
(N.B., I also avoided `Math.transform()`, but I think that one&#39;s worthy of another post because this one is already going to be quite long enough!)
&lt;/blockquote&gt;
&lt;p&gt;This made sense at the time (2015) because, back then, JavaScript performance across the different browsers and platforms varied wildly.&lt;/p&gt;
&lt;p&gt;Chrome’s V8 engine absolutely whupped Firefox’s JS runtime (I forget the name), holding a steady 60fps for &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt;, where Firefox could barely manage 30fps. Safari was also capable of a smooth 60fps, as were both IE11 and Edge.&lt;/p&gt;
&lt;p&gt;And in early 2016 when it came to the prototype version of &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt;, whilst both IE11 and Edge were capable of running the game at 60fps (unlike Firefox), all browsers other than Chrome and Safari took forever to generate the rotated asteroid bitmaps from their vector definitions when the game started up.&lt;/p&gt;
&lt;p&gt;The reasons for these differences go far beyond trig functions (e.g., render performance), but I was searching for any edge I could find to get better performance.&lt;/p&gt;
&lt;p&gt;Four years later is this decision justified? Is it faster to use lookup tables? And even if it is, is it enough of a difference to make a difference?&lt;/p&gt;
&lt;p&gt;Well, not everybody thinks so, and Darren B didn’t think so all the way back in 2012:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/StackOverflow-LookupTablesDeadAndBuriedIn2012.png&quot; alt=&quot;Accepted answer to &#39;Optimization using a lookup table&#39; stating that lookup tables are dead on modern processors.&quot;&gt;&lt;figcaption&gt;Taken from: https://stackoverflow.com/questions/9082040/optimization-using-a-lookup-table&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Given the differences in JavaScript performance across browsers in 2012, this was demonstrably incorrect, even though it’s the accepted answer.&lt;/p&gt;
&lt;p&gt;Note that there is some credence to the comment about cache misses, which we’ll come back to later. Still, even here, at least for some scenarios - and certainly for JavaScript - the case is overstated.&lt;/p&gt;
&lt;p&gt;Some people even claim to have found that pre-computing is slower than using &lt;code&gt;Math.sin()&lt;/code&gt; although in at least some cases that’s because of problems with the benchmark they’ve created. See, for example:&lt;br&gt;
https://stackoverflow.com/questions/45896703/why-is-precomputing-sinx-slower-than-using-math-sin-in-javascript.&lt;/p&gt;
&lt;h3 id=&quot;Let’s-get-down-to-business:-a-naïve-microbenchmark&quot;&gt;Let’s get down to business: a naïve microbenchmark&lt;/h3&gt;
&lt;p&gt;Years ago I had a copy of the 11th Edition of Scott Mueller’s excellent Upgrading and Repairing PCs (TODO: AMAZON AFFILIATE LINK HERE):&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/scott-mueller-upgrading-and-repairing-pcs-11th-edition.jpg&quot; alt=&quot;Cover of Upgrading and Repairing PCs, 11th Edition, by Scott Mueller&quot;&gt;&lt;/figure&gt;
&lt;p&gt;I’m not sure exactly what happened to it but I wish I’d kept it because although the hardware is long out of date (published in 1999) the philosophy behind the processes isn’t.&lt;/p&gt;
&lt;p&gt;In particular Scott talks extensively about performance figures and specifications, and whilst these are all well and good, he points out that the only way to know how much benefit you’ll gain from a given upgrade is to install it and then measure the performance.&lt;/p&gt;
&lt;p&gt;Another way of looking at this is that there is a gap between theory and practice, and this holds as true for software as it does for hardware.&lt;/p&gt;
&lt;p&gt;One of the issues that this throws up - as do the comments I highlighted above - is that you need to be careful &lt;em&gt;what&lt;/em&gt; it is you’re measuring. Even in this relatively naive example, I want to measure something that reflects the reality of my use case. So let’s look at that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Both &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot the Rocks&lt;/a&gt; and &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt; are versions of early vector arcade games: Asteroids and Star Castle, respectively. Such games don’t generally involve bitmap graphics - certainly not in their original incarnations - instead using vectors, which can be freely rotated and scaled, to render objects in the games.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Both use trigonometry extensively for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rotating game objects, such as the player’s spaceship, asteroids, and enemies such as the cannon at the centre of the Star Sastle, or the satellites that chase down the player in Asteroids,&lt;/li&gt;
&lt;li&gt;Calculating and adjusting attack trajectories for enemies such as the rotating cannon, flying saucers, satellites, and homing mines,&lt;/li&gt;
&lt;li&gt;Controlling the maximum speed of in game objects such as the player’s spaceship, mines, flying saucers, and satellites,&lt;/li&gt;
&lt;li&gt;Implementing gravity effects for the black hole in Shoot The Rocks (&lt;a href=&quot;https://en.wikipedia.org/wiki/Spacewar!&quot;&gt;Spacewar!&lt;/a&gt; inspired, rather than something from Asteroids),&lt;/li&gt;
&lt;li&gt;Calculating particle trajectories from rocket exhaust and explosions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Because these are simple arcade games rather than simulations, we don’t need a great deal of precision. Still, any self-respecting Asteroids game or Star Castle clone needs to allow objects to rotate relatively smoothly, so we need a decent number of values in our lookup table. I therefore chose to support integer angles in the range 0 - 359 degrees. This is enough for smooth rotation in an arcade game but obviously nowhwere near enough for any kind of simulation or more serious use. Any non-integer values are rounded down to their nearest integer. This is something we apply to all trig operations in both games. You could even reduce precision further by, for example, using a smaller lookup table. For example, you might not need particularly high fidelity rotation and be able to get away with only storing trig function values for every 10 degrees: 0, 10, 20, 30 … 350. This would mean your array lengths would all be 36 which, even at 64-bit precision, only requires 288 bytes to cache values for each trig function. This equates to 3, 5 or 9 cache lines in your processor’s cache, depending on the size of a cache line. We’ll come back to this point later. Suffice to say for my purposes this is too much of a loss of fidelity, because I want some objects to rotate very slowly - but still smoothly - which is impossible with a small number of &amp;quot;stops&amp;quot; around the circle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We don’t use radians because, although mathematically significant, angles expressed in radians with any precision are almost never whole numbers, and therefore do not make good candidates for array lookup indices even in JavaScript where array indices do not need to be integers. This is because modern JavaScript runtimes generally include optimised handling of arrays with integer indices. However, if we were looking at trig functions as a potential new optimisation, as opposed to re-evaluating an optimisation I put in place years ago, we might choose to evaluate the performance of radian angles versus degree angles versus degree angles with lookup tables for &lt;code&gt;sin&lt;/code&gt;, &lt;code&gt;cos&lt;/code&gt;, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
Actually the original 1979 Asteroids arcade game did *not* rotate the asteroids at all. Asteroids Deluxe, released in 1981, did add rotation but it wasn&#39;t particularly smooth. I haven&#39;t looked into the arcade code for Asteroids Deluxe enough to know whether it used a set of pre-rotated coordinates to render each rotated frame of the asteroids, or whether it calculated those coordinates on the fly using trig lookup tables. I *can* guarantee an arcade game of this vintage, with a 6502 CPU, would have used lookup tables rather than calculate trig functions on the fly!
&lt;/blockquote&gt;
&lt;p&gt;So what we’re going to to is benchmark the performance of &lt;code&gt;Math.sin&lt;/code&gt; versus a lookup table for every integer degree value around the whole circle. And to ensure we can actually measure elapsed time with some confidence, we’re going to go around the circle 1,000,000 times. In other words, we’re going to do 360,000,000 calculations or lookups, measure the time taken, and use the times to compare the performance.&lt;/p&gt;
&lt;p&gt;Here’s the code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;use strict&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RADIANS_IN_A_CIRCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RADIANS_IN_A_CIRCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lookup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lookup&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;count
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; angleDegrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                angleDegrees &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;angleDegrees
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; lookup&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;angleDegrees&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Sin lookup (ES2015+) result: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;result&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Sin lookup (ES2015+) time: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;diff&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;count
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; angleDegrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                angleDegrees &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;angleDegrees
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;angleDegrees &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Math.sin (ES2015+) result: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;result&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Math.sin (ES2015+) time: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;diff&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is pretty easy to run in node or in any web browser, and I’ll provide a link to a web page that does exactly that at the bottom, once we’ve got all our benchmarks in place.&lt;/p&gt;
&lt;p&gt;Before we go any further, note the additions on lines 32 and 57. These are present just to ensure that the loops aren’t optimised away to nothing by the runtime.&lt;/p&gt;
&lt;p&gt;And here’s the ES5 version of the same code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&#39;use strict&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RADIANS_IN_A_CIRCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RADIANS_IN_A_CIRCLE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lookup &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lookup&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doOneMillionLookupsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;count
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; angleDegrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                angleDegrees &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;angleDegrees
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; lookup&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;angleDegrees&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Sin lookup (ES5) result: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;result&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Sin lookup (ES5) time: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;diff&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doOneMillionCalculationsAroundWholeCircle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            count &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;count
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; angleDegrees &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                angleDegrees &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;360&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;angleDegrees
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;angleDegrees &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_DEGREE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Math.sin (ES5) result: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;result&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Math.sin (ES5) time: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;diff&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the only substantive difference is the use of &lt;code&gt;var&lt;/code&gt; rather than &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The reason I’ve separated ES2015+ and ES5 versions is that when I started collecting measurements I was seeting a significant discrepancy in perforance between the two syntaxes when running in node. In fact the ES2015+ version with &lt;code&gt;let&lt;/code&gt; was something like 20x slower than the ES5 version with &lt;code&gt;var&lt;/code&gt; when using a lookup table, and 2x slower when calling &lt;code&gt;Math.sin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I use &lt;code&gt;nvm&lt;/code&gt; and so it turned out I was being an idiot: I was running an older version of node and just needed to switch to the latest version (13.12.0 at the time of writing) to fix that discrepancy. Happily browser performance is consistent between both syntaxes as well so there’s little reason not to use the newer syntax.&lt;/p&gt;
&lt;p&gt;Still, I decided to include benchmarks for both syntaxes, simply for completeness.&lt;/p&gt;
&lt;blockquote&gt;
N.B., Note the use of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode&quot;&gt;strict mode&lt;/a&gt; in both cases. I have found only small, if any, difference between running these benchmarks in strict mode versus running them in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Sloppy_mode&quot;&gt;&amp;quot;sloppy mode&amp;quot;&lt;/a&gt;. Where there were differences at all, strict mode tends to be very slightly faster. All my production code for &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; uses strict mode, so for the sake of brevity (ha!), all benchmarks in this post use strict mode.
&lt;/blockquote&gt;
&lt;p&gt;I’ve also implemented versions of these benchmarks for &lt;code&gt;Math.cos&lt;/code&gt; and &lt;code&gt;Math.tan&lt;/code&gt; but, to avoid scrolling blindness, I haven’t reproduced the code here. You can find it at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Math.cos&lt;/code&gt; versus lookup (ES2015+): https://gist.github.com/bartread/f1160621a59fc8e2fb97be72a04094a0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Math.cos&lt;/code&gt; versus lookup (ES5): https://gist.github.com/bartread/69ce0b25bf0e247856cab7a420843dee&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Math.tan&lt;/code&gt; versus lookup (ES2015+): https://gist.github.com/bartread/f72be52be74fafb9feff70560b42b8b5&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Math.tan&lt;/code&gt; versus lookup (ES5): https://gist.github.com/bartread/47135e9f5437259d1df60bcb72262024&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And here are the results of all these benchmarks:&lt;/p&gt;
&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;col&quot;&gt;Benchmark&lt;/th&gt;
            &lt;th scope=&quot;col&quot;&gt;Node.js / ms&lt;/th&gt;
            &lt;th scope=&quot;col&quot;&gt;Chrome / ms&lt;/th&gt;
            &lt;th scope=&quot;col&quot;&gt;Firefox / ms&lt;/th&gt;
            &lt;th scope=&quot;col&quot;&gt;Safari / ms&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;Sin lookup table (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;296&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;321&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;365&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;297&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Sin lookup table (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;294&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;312&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;368&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;296&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.sin function call (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5114&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5576&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;4538&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;3605&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.sin function call (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5163&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5599&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;4534&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;3833&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Cos lookup table (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;293&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;323&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;369&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;297&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Cos lookup table (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;296&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;323&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;375&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;298&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.cos function call (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5128&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5884&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;4577&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;3818&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.cos function call (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5134&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;6302&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;4559&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;4001&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Tan lookup table (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;294&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;340&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;364&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;304&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Tan lookup table (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;294&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;336&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;371&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;296&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.tan function call (ES2015+)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;8103&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;9204&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5879&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5358&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Math.tan function call (ES5)&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;8086&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;9735&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5912&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;5276&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/BenchmarkFigures-BasicSequentialLoop.png&quot; alt=&quot;Benchmark Figures: Basic Sequential Loop&quot;&gt;&lt;/figure&gt;
&lt;p&gt;As we can see, depending on the trig operation being performed the lookup beats using the trig function by a factor of 12 - 29x, so lookup tables are the obvious winner, right?&lt;/p&gt;
&lt;p&gt;No, not at all.&lt;/p&gt;
&lt;p&gt;This benchmark is absolute nonsense: it’s incredibly naïve and deeply flawed. Let’s look at this in more detail.&lt;/p&gt;
&lt;h3 id=&quot;Unpacking-the-problems-with-our-naïve-microbenchmark&quot;&gt;Unpacking the problems with our naïve microbenchmark&lt;/h3&gt;
&lt;p&gt;The obvious is that  our microbenchmark doesn’t reflect reality of our use case. Let me explain.&lt;/p&gt;
&lt;p&gt;It does millions of sequential array lookups, which modern CPUs are very good at optimising. In particular the processor can ensure that any time we need to do the next array lookup the memory we need to read is already in the L1 cache waiting for us so we don’t need to go out to the L2 or L3 caches or, worst of all, main memory when we do our array lookups.&lt;/p&gt;
&lt;p&gt;Moreover, our entire array of 360 64-bit floating point numbers in theory takes up less than 3K so the whole thing will easily fit into L1 cache. The memory profiler in Chrome Dev Tools suggests it’s actually a bit more than this: 4.5K, but still nowhere near enough to overflow the cache which, as an example, is 32KB (data) per core on an Intel Core i7.&lt;/p&gt;
&lt;p&gt;This means that when we go back around to iterate over the lookup table again (and again, and again), most likely all of it is still sat in the L1 cache, since cache lines are evicted using a least-recently-used algorithm.&lt;/p&gt;
&lt;p&gt;So &lt;em&gt;of course&lt;/em&gt; this is going to be way faster than the browser trig functions which, after all, have to do actual work.&lt;/p&gt;
&lt;p&gt;But our use case is video games, specifically vector arcade games, and this isn’t anything like how we’re going to access our lookup table in our game code.&lt;/p&gt;
&lt;p&gt;We have enemies, projectiles, particles, the player’s ship, and all kinds of following and tracking behaviour between these objects all happening in every single frame, 60 times per second. All of these objects and behaviours need trig, either when they spawn, or when they move, or both. There are tons of trig lookups, or calls to trig functions, but not in a sequential fashion. In fact they’re much closer to completely random. This is much harder for the processor to optimise because its caching logic can’t predict which memory locations it will need to pull into cache next.&lt;/p&gt;
&lt;p&gt;Now with our small lookup table in isolation that may not matter too much. But, of course, it’s not being used in isolation: plenty of other data will be flying in and out of the cache on each frame:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;We two trig lookup tables - both sin and cos - not just one of them for every one of our trig calculations,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There might be hundreds of live objects on screen at any one time in our games, and data relating to all of them will flow through the cache on every frame,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;More data relates to the state of the overall game, as well as controlling the behaviour of classes of objects: level, score, lives left, timestamps, metadata used to configure newly created game objects.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s therefore highly unlikely that our lookup tables will be able to sit undisturbed in L1 cache: there’s a good chance they’ll be evicted. This will mean cache misses the next time we use them, meaning we’ll need to go out to the L2 or L3 caches, or even perhaps main memory, to get the data we need.&lt;/p&gt;
&lt;p&gt;That introduces much more latency.&lt;/p&gt;
&lt;p&gt;How much? Well, as an example, here are the figures for Intel Core I7 Xeon 5500 Series processors:&lt;/p&gt;
&lt;table&gt;
    &lt;caption&gt;Source: https://software.intel.com/en-us/forums/intel-manycore-testing-lab/topic/287236&lt;/caption&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th scope=&quot;col&quot;&gt;Data source&lt;/th&gt;
            &lt;th scope=&quot;col&quot;&gt;Latency (approximate)s&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;L1 cache hit&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~4 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;L2 cache hit&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~10 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;L3 cache hit, line unshared&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~40 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;L3 cache hit, shared line in another core&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~65 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;L3 cache hit, modified in another core&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~75 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Remote L3 cache&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~100 - 300 cycles&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Local DRAM&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~60ns&lt;br&gt;(~216 cycles on 3.6GHz processor)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Remote DRAM&lt;/td&gt;
            &lt;td class=&quot;text-right&quot;&gt;~100ns&lt;br&gt;(~360 cycles on 3.6GHz processor)&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We’re running single-threaded JavaScript so you’d hope we don’t run into too many issues with shared cache lines or remote L3 caches and DRAM but, still, you can see that the latency escalates significantly at each step as we move out through caches and then into main memory.&lt;/p&gt;
&lt;p&gt;For the lookup our benchmark’s tight inner loop is running 360,000,000 times in 290 - 375ms. This means a single iteration runs in 0.8 - 1.04ns. This translates to 3 - 4 CPU clock cycles at 3.6GHz. Looking at the operations being performed (incrementing a count, bounds checking, and adding a value that’s probably in L1 cache to a value that’s probably in a register by the time the JIT has done its magic) I reckon that’s pretty close to as fast as a single CPU core at 3.6GHz can do 360,000,000 lookups and additions without resorting to &lt;a href=&quot;https://en.wikipedia.org/wiki/SIMD&quot;&gt;SIMD&lt;/a&gt; instructions, which we don’t have access to in JavaScript. That’s discounting experimental support, of course.&lt;/p&gt;
&lt;p&gt;By contrast the function calls for &lt;code&gt;Math.sin&lt;/code&gt; and &lt;code&gt;Math.cos&lt;/code&gt; bump the time for a single iteration to anywhere from 12.6ns to 17.5ns, or 45 to 63 clock cycles at 3.6GHz. Note that in this case we’re also multiplying our angle by &lt;code&gt;RADIANS_IN_ONE_DEGREE&lt;/code&gt; to convert from degrees to radians when we call &lt;code&gt;Math.sin&lt;/code&gt; and &lt;code&gt;Math.cos&lt;/code&gt;. However this additional multiplication is a 1 cycle operation on modern Intel CPUs, so it will only contribute 100 - 130ms to the total runtime for the function based benchmark.&lt;/p&gt;
&lt;p&gt;(Note that in all cases &lt;code&gt;Math.tan&lt;/code&gt; cases takes considerably longer but isn’t actually used within any of arcade.ly’s games at present so I’ve only included it for the sake of completeness.)&lt;/p&gt;
&lt;p&gt;In neither case are we doing anything realistic, but the lack of reality is such that it unfairly skews the results in favour of the lookup table approach. Based on the discussion above, then, we need to improve our benchmark to take into account the factors we’ve been talking about:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The fact that at runtime lookups, and calls to &lt;code&gt;Math.sin&lt;/code&gt; and &lt;code&gt;Math.cos&lt;/code&gt;, will occur with non-sequential angles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The need that some use cases will have for a larger (quite possibly much larger) lookup table.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The likelihood of our lookup table being evicted from L1 cache, and possibly L2 and L3 caches as well.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’ll deal with (1) first since it’s pretty simple to remedy.&lt;/p&gt;
&lt;p&gt;It’s also worth bearing in mind that we’re using quite small lookup tables: 360 values is nowhere near enough where real precision is required. We can only get away with it because we’re talking about arcade games, not a simulation of our solar system (for example). If you want to do something that requires a lot more precision you’re either going to have to use the trig functions, or allocate a much bigger lookup table. How much bigger depends on the precision you need, but think thousands, hundreds of thousands, or even millions of values. What would happen to your performance if you needed a really big lookup table?&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <title>How to debug NaN and NaN propagation bugs in JavaScript</title>
    <link href="https://arcade.ly/blog/2020/02/23/how-to-debug-nan-and-nan-propagation-bugs-in-javascript/" />
    <updated>2020-02-23T09:50:43Z</updated>
    <id>https://arcade.ly/blog/2020/02/23/how-to-debug-nan-and-nan-propagation-bugs-in-javascript/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Debugging &lt;code&gt;NaN&lt;/code&gt; (not a number) related bugs in JavaScript can be frustrating. NaNs propagate, which means they can spread themselves around your data structures very quickly under some circumstances, and it can be time-consuming and somewhat tedious to track down the root cause. If you’re just building &lt;a href=&quot;https://en.wikipedia.org/wiki/Create,_read,_update_and_delete&quot;&gt;CRUD&lt;/a&gt; apps you may never encounter any &lt;code&gt;NaN&lt;/code&gt; related issues, but the moment you start working on apps that involves a reasonable amount of numerical manipulation they can start to appear much more regularly. In this post I’ll share what I’ve learned through debugging numerous &lt;code&gt;NaN&lt;/code&gt; related issues I’ve encountered whilst building games for &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this post I’m going to outline some basics (e.g., what is &lt;code&gt;NaN&lt;/code&gt;?), then work through common causes and manifestations of NaN related bugs, describe &lt;code&gt;NaN&lt;/code&gt; propagation with an example, show how you can verify that you do have a &lt;code&gt;NaN&lt;/code&gt; bug, and then look at three different strategies for debugging that you may find helpful.&lt;/p&gt;
&lt;p&gt;So let’s start with the basics: what is &lt;code&gt;NaN&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;NaN&lt;/code&gt; is a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN&quot;&gt;special global property value in JavaScript&lt;/a&gt; whose value means “not a number”.&lt;/p&gt;
&lt;p&gt;Any time you use a variable whose value is &lt;code&gt;NaN&lt;/code&gt; in an expression, the result of that expression with be &lt;code&gt;NaN&lt;/code&gt;, or &lt;code&gt;false&lt;/code&gt; if it was any kind of comparison. You also can’t really do meaningful comparisons with &lt;code&gt;NaN&lt;/code&gt;: for example, the expression &lt;code&gt;NaN === NaN&lt;/code&gt; will always return false.&lt;/p&gt;
&lt;p&gt;Fortunately you &lt;em&gt;can&lt;/em&gt; check for &lt;code&gt;NaN&lt;/code&gt; by calling &lt;code&gt;isNaN(expression)&lt;/code&gt;, which will return &lt;code&gt;true&lt;/code&gt; if and only if the value of expression is &lt;code&gt;NaN&lt;/code&gt;. This will come in handy later on when we look at debugging strategies.&lt;/p&gt;
&lt;p&gt;In a typical CRUD app you may never encounter &lt;code&gt;NaN&lt;/code&gt;, but as soon as you get into any form of numerical manipulation it’s pretty likely you’re going to run into it sooner or later.&lt;/p&gt;
&lt;p&gt;Examples of such apps are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Games&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Particularly anything real-time: arcade games, shooters, platformers - anything that involves real-time movement or collision detection&lt;/li&gt;
&lt;li&gt;Turn-based games can also experience NaN issues where numeric data is involved (RPGs, RTS games, etc.), but their slower pace tends to mean that these bugs are often easier to reproduce and debug: you can often isolate an exact sequence of steps required to reproduce a bug, especially if you can replicate a particular state in the game&lt;/li&gt;
&lt;li&gt;I’ve run into &lt;code&gt;NaN&lt;/code&gt; issues in both my games, &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel (a version of Star Castle)&lt;/a&gt;, and &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks (a version of Asteroids)&lt;/a&gt; which make extensive use of arithmetic, trigonometry, along with basic mechanics and some matrices&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data visualisation&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Such apps will often take a feed of data from the back end, either on demand or in real-time: if that data is manipulated in any way beyond just throwing it at a charting library the possibility of &lt;code&gt;NaN&lt;/code&gt; bugs creeping in exists (in theory it still exists with the library, but the probability of problems arising is much lower), and especially where custom visualisations are used&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Common-causes-of-NaN-related-bugs&quot;&gt;Common causes of NaN related bugs&lt;/h3&gt;
&lt;p&gt;In some sense there are innumerable sources of &lt;code&gt;NaN&lt;/code&gt; bugs but in my experience they tend to fall into a few common classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Divide by zero&lt;/li&gt;
&lt;li&gt;Looking something up in an array using an index value that doesn’t exist, like a non-integral for looking up corresponding values that are approximated for their integral indices, or using a negative index rather than forcing it to positive (e.g., by adding 360 degrees)&lt;/li&gt;
&lt;li&gt;Using an object in arithmetic rather than one of its properties (check the commit log for examples!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;1.-Divide-by-zero&quot;&gt;1. Divide by zero&lt;/h4&gt;
&lt;p&gt;You attempted to &lt;a href=&quot;https://en.wikipedia.org/wiki/Division_by_zero&quot;&gt;divide a number by zero&lt;/a&gt;, and since the result of division by zero is undefined, the result of your division is &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In some languages and platforms - notably those that are “strongly typed” - you’d see something like a &lt;code&gt;DivideByZeroExeption&lt;/code&gt; thrown here. For example, the .NET runtime will throw a &lt;code&gt;DivideByZeroException&lt;/code&gt; but &lt;em&gt;only&lt;/em&gt; for integral or decimal types. It will not throw it for arithmetic involving 32-bit (&lt;code&gt;float&lt;/code&gt;) or 64-bit (&lt;code&gt;double&lt;/code&gt;) floating point numbers, since these have special values representing NaN &lt;a href=&quot;https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/&quot;&gt;as defined by the IEEE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;JavaScript is “weakly typed”, which means that any variable can be of any type, and can even change its type. For example, defining a variable as &lt;code&gt;var myVariable = 1;&lt;/code&gt; and then following up with an assignment like &lt;code&gt;myVariable = &#39;wibblefarb&#39;;&lt;/code&gt; is entirely acceptable.&lt;/p&gt;
&lt;p&gt;In terms of numbers therefore, JavaScript doesn’t have any distinction between integer and floating point values: strictly speaking all numbers are represented as 64-bit floating point values. These of course have that special representation for &lt;code&gt;NaN&lt;/code&gt;. Hence, as aggravating as it may be, there is no exception or error if you do something like dividing by zero that results in a &lt;code&gt;NaN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Worth pointing out that there’s nothing stopping JavaScript runtimes, such as Google’s &lt;a href=&quot;https://v8.dev/&quot;&gt;V8&lt;/a&gt; or Mozilla’s &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey&quot;&gt;SpiderMonkey&lt;/a&gt;, from representing numbers as integers under the hood as an optimization, but exposing them as 64-bit floats when needed, but this isn’t something visible to you as a programmer.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Nobody intentionally divides by zero, but there are plenty of ways it can happen inadvertently, so let’s look at a simple trigonometry example based on a common scenario in many games.&lt;/p&gt;
&lt;p&gt;In both of my online arcade games there are enemies that need to track the player’s spaceship and rotate to face the player, either so they can chase the player down, or fire directly at them.&lt;/p&gt;
&lt;p&gt;In both cases we know where the enemy is, and we know where the player is, and we need to use these facts to determine the angle of rotation required for the enemy to point directly at the player.&lt;/p&gt;
&lt;p&gt;Here, in &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks (Asteroids)&lt;/a&gt;, the flying saucer needs to know the angle it should fire at to hit the player’s space ship.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/Asteroids_FlyingSaucerHuntingThePlayer_72dpi.jpg&quot; alt=&quot;Flying saucer targetting the player&#39;s spaceship in Asteroids/Star Castle.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;We need to know the angle &lt;code&gt;A&lt;/code&gt;, relative to straight up, so that the flying saucer can fire at the player. In reality we’re going to add some random error to this, otherwise the game would be too hard, but we start with knowing where we should shoot. Note that Shoot The Rocks isn’t very sophisticated, so there’s no sense of the flying saucers shooting at where the player is most likely to be when the bullet reaches them: they just target where the player is now.&lt;/p&gt;
&lt;p&gt;In the next shot, again from &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks/Asteroids&lt;/a&gt;, the satellites home in the player, so the satellite needs to know which direction to head in to eventually collide with the player’s spaceship.&lt;/p&gt;
&lt;p&gt;TODO: SCREENSHOT HERE&lt;/p&gt;
&lt;p&gt;Again, the angle A shows the direction, relative to straight up, that our satellite needs to be headed in.&lt;/p&gt;
&lt;p&gt;TODO: SCREENSHOT HERE&lt;/p&gt;
&lt;p&gt;Finally, in &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel (Star Castle)&lt;/a&gt; the central cannon needs to track the player’s spaceship so that it can fire at the player whenever it gets a clear line of sight.&lt;/p&gt;
&lt;p&gt;TODO: SCREENSHOT HERE&lt;/p&gt;
&lt;p&gt;These behaviours are all slightly different, and I might talk about them in more detail in future, but the key point is finding that angle A, and this is where the trigonometry comes in.&lt;/p&gt;
&lt;p&gt;To work out A we actually need to know both the sine of A, and the cosine of A. This is because the values of both sine and cosine are periodic oscillations: they repeat every 360 degrees and, for most values of both, there are two possible matching angles.&lt;/p&gt;
&lt;p&gt;As you can see from the following diagram, sine is 0.5 at both 30° and 150°:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/SineAndCosinePlotted0To360Degrees.png&quot; alt=&quot;Sine and cosine functions plotted for angles from 0° to 360° inclusive.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;As the chart also shows, sine and cosine are out of phase by 90 degrees, so if we have the values of both functions we can unambiguously resolve the corresponding angle. In fact we really only need to know if the cosine is positive, negative or zero to be able to pick the correct angle that matches the sine.&lt;/p&gt;
&lt;p&gt;You might remember, from a mnemonic or rhymn at school (SOHCAHTOA, or whatever), that sine and cosine are defined for angle A as follows:&lt;/p&gt;
&lt;figure&gt;&#92;[sin A = &#92;frac{Opposite}{Hypotenuse}&#92;]&lt;/figure&gt;
&lt;figure&gt;&#92;[cos A = &#92;frac{Adjacent}{Hypotenuse}&#92;]&lt;/figure&gt;
&lt;p&gt;Both of these equations can lead to instances of division by zero where the length of &#92;(Hypotenuse&#92;) is 0.&lt;/p&gt;
&lt;p&gt;This will occur when the coordinates of, say, the flying saucer and the player’s ship, or the cannon and the player’s ship, are the same, because the length of the &#92;(Hypotenuse&#92;) is defined by Pythagoras’ theorem:&lt;/p&gt;
&lt;figure&gt;&#92;[Hypotenuse^2 = Opposite^2 + Adjacent^2&#92;]&lt;/figure&gt;
&lt;p&gt;which rearranges to:&lt;/p&gt;
&lt;figure&gt;&#92;[Hypotenuse = &#92;sqrt{Opposite^2 + Adjacent^2}&#92;]&lt;/figure&gt;
&lt;p&gt;Hopefully your collision detection will have kicked in but you can’t guarantee that this will be the case if both objects are moving fast and, of course, depending on the order in which you choose to detect collisions and select firing solutions.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Talk about classes that TypeScript can help avoid&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;Common-manifestations-of-NaN-related-bugs-experienced-by-users&quot;&gt;Common manifestations of NaN related bugs experienced by users&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;What-is-NaN-propagation-and-how-does-it-occur?&quot;&gt;What is NaN propagation and how does it occur?&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;How-do-you-tell-objectively-when-you-have-a-NaN-bug?&quot;&gt;How do you tell objectively when you have a NaN bug?&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
&lt;h3 id=&quot;Strategies-for-debugging-NaN-related-bugs-(only-one-of-which-I-have-been-able-to-use-successfully)&quot;&gt;Strategies for debugging NaN related bugs (only one of which I have been able to use successfully)&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/construction/mamagnolia_acresunderconstruction.gif&quot; alt=&quot;This section is still under construction.&quot;&gt;&lt;/figure&gt;
</content>
  </entry>
  <entry>
    <title>Shoot The Rocks: tips and tricks for dealing with black holes in my version of Asteroids</title>
    <link href="https://arcade.ly/blog/2020/02/01/shoot-the-rocks-tips-and-tricks-for-dealing-with-black-holes-in-my-version-of-asteroids/" />
    <updated>2020-02-01T11:26:05Z</updated>
    <id>https://arcade.ly/blog/2020/02/01/shoot-the-rocks-tips-and-tricks-for-dealing-with-black-holes-in-my-version-of-asteroids/</id>
    <content type="html">&lt;p&gt;There are a few things about the modern variant of &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; that aren’t really reflective of the original arcade games on which it’s based.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; is perhaps most similar to &lt;a href=&quot;https://en.wikipedia.org/wiki/Asteroids_Deluxe&quot;&gt;Asteroids Deluxe&lt;/a&gt;, which is a fairly basic evolution of the original &lt;a href=&quot;https://en.wikipedia.org/wiki/Asteroids_(video_game)&quot;&gt;Asteroids&lt;/a&gt;. We still don’t have the satellites in &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; yet: I do intend to add them, it’s just a case of finding the time.&lt;/p&gt;
&lt;p&gt;The greater use of colour is reminiscent of &lt;a href=&quot;https://en.wikipedia.org/wiki/Space_Duel&quot;&gt;Space Duel&lt;/a&gt;, and the (soon to be implemented) availability of power-ups is somewhat reminiscent of &lt;a href=&quot;https://en.wikipedia.org/wiki/Blasteroids&quot;&gt;Blasteroids&lt;/a&gt; - although my power-ups are very much focussed on fairly simple weapons upgrades only.&lt;/p&gt;
&lt;p&gt;I suppose I do like to think that &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; has its own character though, particularly when the modern variant is played. The graphics, whilst similar and very much tipping a hat to the original vector games, are different from the original, as are the game dynamics and some of the features.&lt;/p&gt;
&lt;p&gt;For example, something that doesn’t appear in any of the arcade games or their ports, as far as I’m aware, are &lt;a href=&quot;https://en.wikipedia.org/wiki/Black_hole&quot;&gt;black holes&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/asteroids-black-hole-near-ship.jpg&quot; alt=&quot;A black hole - admittedly more of a blue hole - shown near the player&#39;s ship during a game of Shoot The Rocks. Note the asteroids in close proximity&quot;&gt;&lt;figcaption&gt;Note the black hole (yes, admittedly it&#39;s blue, but I felt like it needed to be visible to keep the game fair!) below the player&#39;s ship. Note also that it&#39;s drawn a number of asteroids towards it. It took me several attempts to get this screenshot as my ship kept getting destroyed by asteroids that were being sucked in towards the black hole (see tips below).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These are very much based on the star that appeared in the grandsire of all shooters, &lt;a href=&quot;https://en.wikipedia.org/wiki/Spacewar!&quot;&gt;Spacewar!&lt;/a&gt;, in that they implement a &lt;a href=&quot;https://en.wikipedia.org/wiki/Gravity_well&quot;&gt;gravity well&lt;/a&gt; that influences the movement of all other objects in the game.&lt;/p&gt;
&lt;p&gt;They add another dimension to the game, which you can either use to your advantage, or which can be your undoing, depending upon how you deal with them.&lt;/p&gt;
&lt;p&gt;So here are some tips and tricks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Your ship is fitted with intertial stabilisers: this is why it will eventually come to a halt when no thrust is applied. It also means that, unlike most other game objects, your ship has &lt;em&gt;some&lt;/em&gt; capacity to resist the pull of black holes. Eventually, when that pull becomes too much, your ship will get sucked towards the black hole. You’ll notice that flying saucers are also less affected by black holes than, for example, asteroids, because they too are fitted with inertial stabilisers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Contact with black holes is not in itself fatal, however if you get too close your ship can either be captured (albeit temporarily), or more likely accelerated around the black hole and slingshotted off unpredictably. This can leave you in the direct path of other hazards, such as asteroids or flying saucers, and lead very quickly to your demise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It’s advisable to give black holes a wide birth, both because of their tendency to cause your ship to accelerate unpredictably, and also because they draw other objects - particularly inanimate objects such as asteroids - towards themselves. This means that you can find yourself in the middle of a vortex of coalescing rocks from which you won’t be able to escape, except by using your always risky hyperspace capability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a black hole is around, having fewer other objects on the screen is generally better due to the tendency for the black hole to slingshot these objects around in unpredictable ways. I.e., try to avoid a situation in which you have shot all the larger asteroids and turned them into a much larger number of smaller asteroids, otherwise you’re probably going to be quickly annihilated in a collision. Similarly, if you only have a few smaller asteroids left at the end of a level, perhaps draw out completion of that level for as long as possible, or avoid shooting too many larger asteroids when the next level begins.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you see lots of asteroids being drawn towards the black hole at once this can be a good opportunity to quickly clean up by shooting rapidly directly at the black hole. Hopefully your shots and the asteroids will be drawn together in the black hole’s gravity well leading to mass destruction. Do keep yourself at a safe distance so you have some chance of avoiding any errant rocks that are thrown out of the gravity well towards you.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully these tips will help you effectively take advantage of the presence of black holes, without falling foul of their dangers.&lt;br&gt;
&lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Play Shoot The Rocks now&lt;/a&gt; to try them out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Making gravity fun: adding black holes to Shoot The Rocks, my version of Asteroids</title>
    <link href="https://arcade.ly/blog/2020/01/25/making-gravity-fun-adding-black-holes-to-my-version-of-asteroids/" />
    <updated>2020-01-25T22:15:17Z</updated>
    <id>https://arcade.ly/blog/2020/01/25/making-gravity-fun-adding-black-holes-to-my-version-of-asteroids/</id>
    <content type="html">&lt;p&gt;Well, it’s been a while (!) since my last blog post here but, you know, life intervenes. Still, I’ve started working on the site and improving the games again.&lt;/p&gt;
&lt;p&gt;I had this idea that I wanted to inject a little chaos and variety into &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; by adding black holes with associated gravity effects. This is something the original Asteroids didn’t have, and I don’t believe they’re in Asteroids Deluxe either. Still, I thought they might make for a fun addition.&lt;/p&gt;
&lt;p&gt;If you want to jump straight in an see what effect this has all had on the game, &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;just click here to play&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I want the classic version of the game to be quite close to the original arcade game so black holes only appear in the modern game variant. (I’ll probably add satellites to both classic and modern variants though because Shoot The Rocks is more like Asteroids Deluxe than the original Asteroids!)&lt;/p&gt;
&lt;p&gt;Whilst black holes didn’t take ages to implement - really only a couple of afternoons - the road wasn’t totally straight. Much of the time was spent tweaking behaviour until it felt right: it’s more important for any game mechanic I add to be fun than it is for it to be realistic.&lt;/p&gt;
&lt;p&gt;Gravity is obviously a real phenomenon so I did start off with a fairly realistic model, in the form of Newton’s Law of Universal Gravitation:&lt;/p&gt;
&lt;figure&gt;&#92;[F = G&#92;frac{m_1m_2}{r^2}&#92;]&lt;/figure&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&#92;(F&#92;) is the force in Newtons&lt;/li&gt;
&lt;li&gt;&#92;(G&#92;) is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gravitational_constant&quot;&gt;gravitational constant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&#92;(m_1&#92;) is the mass of our black hole&lt;/li&gt;
&lt;li&gt;&#92;(m_2&#92;) is the mass of whatever other game object we’re interested in&lt;/li&gt;
&lt;li&gt;&#92;(r&#92;) is the distance between the black hole and the other object &lt;em&gt;(Strictly, I believe, it’s the radius of the circle whose origin is the centre of mass of the black hole and whose edge would intersect the centre of mass of the other object, or vice versa. Details, details, etc.)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In simple terms, the gravitational force between two objects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is proportional to both their masses (actually to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Product_(mathematics)&quot;&gt;product&lt;/a&gt; of their masses)&lt;/li&gt;
&lt;li&gt;Gets weaker the further apart the objects are: i.e., it’s inversely proportional to the &lt;em&gt;square&lt;/em&gt; of the distance between them (known as an inverse square law)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Granted, I could have gone more realistic, but worrying about relativity or quantum gravity in a game based on something as inherently unrealistic as an homage to Atari’s Asteroids is clearly ludicrous, and Newton’s law has the advantage of being simple to get to grips with.&lt;/p&gt;
&lt;p&gt;Also, I kind of knew from the outset that any changes to increase the fun factor would most likely to simplifications and corners cut, so it makes sense to start off with something simple to begin with. We need to get to the point where gravity effects feel like they fit in with the rest of the game which, to begin with, has very simple mechanics.&lt;/p&gt;
&lt;p&gt;The main things to grasp are that the heavier objects are, the stronger the gravitational force between them, and the closer together they are the stronger the force is. The reverse is also true.&lt;/p&gt;
&lt;p&gt;Hopefully that’s fairly intuitive and, as you’ll see, the specifics of the equation aren’t really that important from a playability and fun perspective.&lt;/p&gt;
&lt;p&gt;Something else to mention is that I want gravity to affect most, if not all, objects on screen when a black hole is present:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The player’s ship&lt;/li&gt;
&lt;li&gt;Asteroids&lt;/li&gt;
&lt;li&gt;Bullets&lt;/li&gt;
&lt;li&gt;Flying saucers (and other enemy craft that we might eventually implement)&lt;/li&gt;
&lt;li&gt;Particles&lt;/li&gt;
&lt;li&gt;Any power-up collectables we implement&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s get down to business and talk about how I made this work.&lt;/p&gt;
&lt;h4 id=&quot;Simplification,-simplification,-simplification&quot;&gt;Simplification, simplification, simplification&lt;/h4&gt;
&lt;p&gt;Firstly, we’re going to assume that the mass of every object other than the black hole is 1. This simplifies Newton’s law to:&lt;/p&gt;
&lt;figure&gt;&#92;[F = G&#92;frac{m_1}{r^2}&#92;]&lt;/figure&gt;
&lt;p&gt;Secondly, we’re going to assume that the gravitational effect of these objects on the black holes, and on eachother, is zero. I.e., black holes are the only objects that exert any gravitational influence. This massively reduces the number of calculations we need to perform.&lt;/p&gt;
&lt;p&gt;Thirdly, as I mentioned, G is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gravitational_constant&quot;&gt;gravitional constant&lt;/a&gt;. This is a very small number indeed: approximately &#92;(6.674&#92;times10^{-11} m{^3}&#92;cdot kg^{-1}&#92;cdot s^{-2}&#92;). It’s kind of aggro so we’re just going to throw it away and pretend it never existed, which yields:&lt;/p&gt;
&lt;figure&gt;&#92;[F = &#92;frac{m_1}{r^2}&#92;]&lt;/figure&gt;
&lt;p&gt;We’re also going to bin off the concept of force, and substitute acceleration straight in there.&lt;/p&gt;
&lt;figure&gt;&#92;[A = &#92;frac{m_1}{r^2}&#92;]&lt;/figure&gt;
&lt;p&gt;And this is really where I started with my implementation. I also precalculated everything and chucked it in a lookup table because, of course, we need to separate acceleration along the x and y axes, which requires some trigonometry.&lt;/p&gt;
&lt;p&gt;Still, what I found sticking strictly to the inverse square law from Newton is that the gravitational influence of the black holes fell off too quickly. I could, of course, increase the mass of the black holes, but this led to absolutely out of control acceleration of objects very close to them, which also wasn’t what I wanted.&lt;/p&gt;
&lt;p&gt;This meant I tried the following:&lt;/p&gt;
&lt;figure&gt;&#92;[A = &#92;frac{m_1}{r}&#92;]&lt;/figure&gt;
&lt;p&gt;I.e., rather than an inverse square law, I simplified to a basic inverse law. Unfortunately this now meant that the gravitational effects were felt too strongly on more distant objects, which in practice was equally undesirable, and felt kind of weird.&lt;/p&gt;
&lt;p&gt;In the end, after a bit more fiddling around, I settled on the following:&lt;/p&gt;
&lt;figure&gt;&#92;[A = &#92;frac{m_1}{r^{1.5}}&#92;]&lt;/figure&gt;
&lt;p&gt;I.e., an inverse square law is an instance of a more general inverse power law, and what I’ve done here is simply to reduce the power to a level where for our purposes it gives a nice balance of stronger affect at close range versus still having some effect at greater distances.&lt;/p&gt;
&lt;p&gt;And that means we now have some sort of pseudo-gravity effect that injects some chaos and fun, whilst also working well with the three sizes of black hole I’ve implemented (small, medium, and large). Large black holes are now just about powerful enough to capture flying saucers, but not powerful enough to permanently capture the player’s ship, which is really what I wanted.&lt;/p&gt;
&lt;p&gt;They also exert a pleasing, but not out of control ridiculous influence on all other game objects.&lt;/p&gt;
&lt;p&gt;As I write this I’m sat approximately 12 miles from Trinity College in Cambridge where Newton was a fellow. Were he still alive, and aware of what I’ve done here (especially given the computing power available even within the average smartphone), I imagine he might have a few stern words to say to me the next time I venture into town.&lt;/p&gt;
&lt;p&gt;Still, if you’re of a more forgiving bent and want to have some fun by playing the game and seeing how this has all worked out &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;then click here to play&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Perceived performance part 2: load assets asynchronously or on-demand</title>
    <link href="https://arcade.ly/blog/2017/03/06/perceived-performance-part-2-load-assets-asynchronously-or-on-demand/" />
    <updated>2017-03-06T13:28:41Z</updated>
    <id>https://arcade.ly/blog/2017/03/06/perceived-performance-part-2-load-assets-asynchronously-or-on-demand/</id>
    <content type="html">&lt;p&gt;When I started my &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; side-project, for such it is, I had a variety of motivations. One of them was that I was a bit fed up of all these low-rent Flash versions of classic arcade games that I’d loved as a kid tarnishing my nostalgia with their long load times, pages covered with ads (yes, there are ads on this site, because I want it to pay for itself, but I try to keep things tasteful), and generally sub-par experience.&lt;/p&gt;
&lt;p&gt;I even felt pretty smug and superior about the idea of building games with HTML5, CSS, and JavaScript that would load &lt;strong&gt;instantly&lt;/strong&gt;, or pretty close to, compared with their bloated Flash equivalents with their interminable loading progress bars. Then, of course, reality came calling, complete with its usual payload of disappointment - something &lt;a href=&quot;https://news.ycombinator.com/item?id=13062775&quot;&gt;I posted about in a comment on Hacker News&lt;/a&gt;. There I identified some fairly obvious reasons why games can take a while to load that go some way beyond “Flash is an awful nineties/noughties horrendo-tech abortion that should be blown into charred corpse fragments with dynamite”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Loading games assets takes time, particularly on slower connections (like mobile)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Graphics (background images, sprites, textures, logos, etc.)&lt;/li&gt;
&lt;li&gt;Sound effects&lt;/li&gt;
&lt;li&gt;Music&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;Maps and other metadata (game objects, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating procedurally generated content can take a while&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now this is frustrating, because it turns out these are big problems for me in that I have a combination of downloaded assets and procedurally generated content. So how do I get around this?&lt;/p&gt;
&lt;p&gt;It’s really a combination of two techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Asynchronous loading of non-essential large assets after DOM content has loaded, and the page has rendered, which happens immediately after.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Immediate feedback to the user that something is happening upon page rendering.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Neither of these results in anything being loaded any quicker, but it does get the loading &amp;quot;out of the way&amp;quot;, and at least shows the user that something is going on whilst the procedurally generated content is being created. I’ll talk about (1) in detail below, and address (2) in a follow-up post.&lt;/p&gt;
&lt;p&gt;Now both Star Castle and Asteroids are vector based arcade games so in my versions, &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt; or &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt;, we don’t need to worry about downloading any bitmaps or textures for either. The graphics for all the game objects - spaceships, asteroids, flying saucers, mines, etc. - are specified using a simple vector-based notification as JavaScript objects, so they’re just part of the source code. These specifications are just based on designs I knocked up quickly in &lt;a href=&quot;https://www.omnigroup.com/omnigraffle&quot;&gt;OmniGraffle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using JavaScript objects is not really super-efficient in terms of space but uglify does a good-enough job of compressing them, and they’re not very large or complex anyway. The player’s ship in Star Castle is 8 blue lines, for crying out loud!&lt;/p&gt;
&lt;p&gt;If I did have a lot of bitmaps, textures, pixel art, 3D models, or whatever, I’d probably have to load a lot of them upfront because they’d be needed to actually play the game, and there’s not much avoiding that. The one thing I could do, if I were using different graphics for different levels, for example, is load only a minimal set of graphics up front, and then load the rest on demand later on.&lt;/p&gt;
&lt;p&gt;I also don’t have to worry about maps, which are another potentially sizeable asset. Again though, I could load them on demand for each level or area as required rather than all in one go.&lt;/p&gt;
&lt;p&gt;This deferred loading is a big advantage over Flash games which, at least in most of the cases I see, tend to load most of their assets in one go upfront. I’m not sure if this is a necessity or just a case of bad architecture. Given my somewhat snarky &amp;quot;low-rent&amp;quot; comment earlier I’d guess at bad architectural choice. I don’t know Flash at all, but it’s quite hard to imagine that Adobe wouldn’t have included some equivalent of JavaScript’s XHR in it somewhere, to allow content to be downloaded in the background.&lt;/p&gt;
&lt;p&gt;What I do have in terms of large assets are sound effects and music. These are obviously compressed, and I use MP3s because they have the best across-the-board browser compatibility of any compressed audio format. They also work with both the HTML5 &lt;code&gt;&amp;amp;lt;audo&amp;amp;gt;&lt;/code&gt; element, required for IE11 compatibility, and the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API&quot;&gt;Web Audio API&lt;/a&gt;, which is &lt;a href=&quot;http://caniuse.com/#feat=audio-api&quot;&gt;well supported across other browsers&lt;/a&gt;. I’d prefer to use Ogg Vorbis because it’s more efficient but the browser support just isn’t there unfortunately.&lt;/p&gt;
&lt;p&gt;Sound effects don’t take up much room, currently weighing in at less than 200K for both games, although that’s set to increase because I haven’t created all the sound effects I need yet.&lt;/p&gt;
&lt;p&gt;Sadly music is a different story. Both games use three tracks: one for the title/main menu screen, another whilst playing the game, and a final track for the game over screen. These weigh in at 6-7MB total for each game on desktop, or 4-6MB for the lower quality mono versions I serve to mobile devices and tablets.&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-reverse&quot;&gt;
_**BEST PRACTICE: To improve REAL performance of your website across a range of devices you should consider serving more optimised assets to mobile users.**_
&lt;/blockquote&gt;
&lt;p&gt;This is obviously problematic, particularly over lower bandwidth mobile connections. Still, whilst sound effects and music add to the experience of playing a game, they’re not actually essential: you &lt;em&gt;can&lt;/em&gt; play the game without them. Therefore I load sound effects and music asynchronously in order of priority as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Sound effects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In-game music.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All other music.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The priority is just about the order the requests are submitted in and is most beneficial over slower connections, and those using HTTP 1.1 as opposed to HTTP2. Fortunately nowadays I only have to worry about serving HTTP 1.1 to IE11 on Windows 8.1 and earlier, and Opera Mini (which I don’t care about anyway). I decided to drop support for IE10 because, although games are/were playable, I have no reliable way to test against IE10 (and Microsoft themselves &lt;a href=&quot;https://www.microsoft.com/en-gb/windowsforbusiness/end-of-ie-support&quot;&gt;dropped support for it some time ago&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The prioritisation is somewhat obvious: sound effects are clearly the most important for the game-playing experience so it makes sense to load them first, but why load the in-game music before the title screen/main menu music?&lt;/p&gt;
&lt;p&gt;Simply because on iOS devices web audio is disabled until it gets triggered by a user interaction, like tapping a button. I have all my buttons set up to enable audio, in addition to whatever other action they trigger. Since the first thing a player is likely to do is tap the &lt;strong&gt;Play&lt;/strong&gt; button, I load the in-game music before any other music even though the title screen appears first.&lt;/p&gt;
&lt;p&gt;This all leads to what I’d suggest is another best practice:&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-reverse&quot;&gt;
_**BEST PRACTICE: Any asset that isn&#39;t absolutely essential to use your website should be loaded later, or not loaded at all.**_
&lt;/blockquote&gt;
&lt;p&gt;This shot of Chrome Dev Tools’ Network tab shows how this plays out in practice:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/star-castle-load-sfx-async.png&quot; alt=&quot;Loading Star Citadel visualised in Chrome Dev Tools. Note how all the DOM content loads and the page renders before we start loading sound effects.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;I’ve used &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt; as the example here, simply because &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt; does a lot of image creation, and these images show up as resources in the Network tab even though they’re generated locally.&lt;/p&gt;
&lt;p&gt;Now this is over a fairly slow WiFi connection in the Swiss Alps, which has lower bandwidth (and is somewhat laggier) than my broadband connection back home. But you can see that the DOM content is loaded, and the page rendered, in under 600ms (at home this would be less than 200ms). You can see that we don’t start loading sound effects (the bottom five rows) until after the render.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I mentioned in my &lt;a href=&quot;https://arcade.ly/blog/2017/03/04/perceived-performance-part-1-what-is-perceived-performance/&quot;&gt;previous post&lt;/a&gt; that I’d highlight areas where improvement is still needed, and this is one of them.&lt;/p&gt;
&lt;p&gt;In all games it’s possible to enable or disable music and sound effects, and this can be done independently, as you can see from this screenshot of the main menu in &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot The Rocks&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;
![Shoot The Rocks main menu showing sound and music configuration, with a few leftover asteroids from the previous game still floating around](/images/blog/asteroids-game-music-sfx-configuration.jpg)
&lt;/a&gt;
&lt;figcaption&gt;
Sound configuration in Shoot The Rocks - note the two toggle buttons at the bottom of the menu. In this case sound effects are enabled and music is disabled. (You can clearly tell I&#39;ve been playing a game when I should have been writing this post because there are asteroids flying around behind the menu. Busted!)
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now this is grand but I really should load sound effects and music only when the corresponding setting is enabled, whereas at the moment I just load them anyway, which isn’t so great. This is particularly important for mobile users, who might be on capped or metered data plans.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Moving on, let’s take a look at some code to see how I’ve achieved this deferred loading.&lt;/p&gt;
&lt;p&gt;Firstly, sound effects and music are just configured directly in the source code as a bunch of descriptors, which look like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;use strict&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    pinjector
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;starCastle&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;factory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&#39;soundConfiguration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token string&quot;&gt;&#39;baseAsteroidsStarcastleSoundConfiguration&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                soundConfiguration
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;soundConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;baseAsteroidsStarcastleSoundConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;sounds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cannonThrust&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/CannonThrust.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cannonFire&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/CannonFire.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cannonLock&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/CannonLock.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.4&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;mineRelease&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/MineRelease.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;rapidRepeat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;mineCapture&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/MineCapture.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;rapidRepeat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;alienScream&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/sounds/starcastle/AlienScream.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;rapidRepeat&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;music&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;titleMusic&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/179_full_arrested-for-driving-while-mad_0159.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;srcLowQuality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/179_full_arrested-for-driving-while-mad_0159_lowquality.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;autoplay&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;levelMusic&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/179_full_a-naked-gun-bank-assault_0100.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;srcLowQuality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/179_full_a-naked-gun-bank-assault_0100_lowquality.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gameOverMusic&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/156_full_good-ol-western_0123.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;srcLowQuality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/music/starcastle/156_full_good-ol-western_0123_lowquality.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;volume&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        baseAsteroidsStarcastleSoundConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mergeIntoMaster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; baseAsteroidsStarcastleSoundConfiguration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/*
This code is licensed under the terms of the MIT License as described
in LICENSE.txt.

Copyright (c) 2015 - 2017 Bart Read, arcade.ly (https://arcade.ly),
and bartread.com Ltd (http://www.bartread.com/)

The soundConfiguration service is a per-game service, implemented by
games at https://arcade.ly, that provides configuration for sound
effects and music to the sfx service, thus allowing them to be played.
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each game has its own version of this configuration.&lt;/p&gt;
&lt;p&gt;You see the &lt;code&gt;config&lt;/code&gt; object has two array members, &lt;code&gt;sounds&lt;/code&gt; and &lt;code&gt;music&lt;/code&gt;. Each of these is a collection of descriptors, most of which are hopefully pretty self-explanatory. Note that music can (but does not have to) have two sources, one of which is a lower quality file for mobile devices. Sound effects have so far been small enough that I haven’t bothered with lower quality versions, although I might support them in future.&lt;/p&gt;
&lt;p&gt;Apart from the overall prioritisation I’ve described above, XHRs to load sounds are fired off in the order they appear in this lists, so if some sounds are more important than others they should just be listed first.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Btw, if you’re wondering what &lt;code&gt;pinjector&lt;/code&gt; is, it’s a very simple, and rather cruddy injector I wrote that behaves somewhat like the Angular 1.x injector. The reason for its existence is simple: when I started writing Star Citadel I was, for literally no good reason, using Angular. I quickly realised this was madness, so ditched Angular but couldn’t be bothered to rewrite all the service boilerplate. This is a decision I now regret. If you really want the source code for it you can find it &lt;a href=&quot;https://gist.github.com/bartread/ee632195cdc1a40e5909abf05f96547e&quot;&gt;here&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And, using these descriptors, we load the sounds with something like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_initialiseSoundEffects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;_loadSounds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;soundConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sounds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;_loadSounds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;soundConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;music&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_loadSounds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;sounds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; areSfx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; soundIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; soundCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sounds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; soundIndex &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; soundCount&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;soundIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sound &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sounds&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;soundIndex&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;areSfx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;elementIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;                        
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        service&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createGain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;volume &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; soundConfiguration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sfxMasterVolume&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;_loadSound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sound&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_loadSound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;sound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;XMLHttpRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&#39;GET&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPhone &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcLowQuality
            &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcLowQuality
            &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;arraybuffer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decodeAudioData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            sound&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; buffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/*
This code is licensed under the terms of the MIT License as described
in LICENSE.txt.

Copyright (c) 2015 - 2017 Bart Read, arcade.ly (https://arcade.ly),
and bartread.com Ltd (http://www.bartread.com/)

The soundConfiguration service is a per-game service, implemented by
games at https://arcade.ly, that provides configuration for sound
effects and music to the sfx service, thus allowing them to be played.
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is perhaps a bit more code than we need for this example (I literally copied and pasted it verbatim from the &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; source) but the key point to take away is that each descriptor is populated with a gain module, which is connected to the sound destination, and then a buffer containing the sound itself, which is loaded asynchronously via XHR.&lt;/p&gt;
&lt;p&gt;We can then play sound effects using these functions:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;effect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;areSfxEnabledFlag &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;effect&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; effect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createBufferSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; buffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    effect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mediaSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;effect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;noteOn&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/*
This code is licensed under the terms of the MIT License as described
in LICENSE.txt.

Copyright (c) 2015 - 2017 Bart Read, arcade.ly (https://arcade.ly),
and bartread.com Ltd (http://www.bartread.com/)

The soundConfiguration service is a per-game service, implemented by
games at https://arcade.ly, that provides configuration for sound
effects and music to the sfx service, thus allowing them to be played.
*/&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_playByName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;_play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how, if sound effects are disabled, the effect doesn’t exist, or the effect doesn’t contain a buffer (i.e., the sound effect hasn’t yet been loaded) we simply return without doing anything.&lt;/p&gt;
&lt;p&gt;I posted a shot from the Network tab in Chrome Dev Tools showing how this all behaves, but what’s quite cool is that you can test out what happens for yourself. It really becomes much clearer if you apply throttling to simulate a slow connection, because then you can see that the games still work without sound effects and music being completely loaded. To do this, open up Chrome Dev Tools and so the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Switch to the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; dropdown towards the right of the toolbar select &lt;strong&gt;GPRS (500ms, 50kb/s)&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Point your browser window at one of the following URLs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Star Citadel (Star Castle) - https://arcade.ly/games/starcastle/&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shoot the Rocks (Asteroids) - https://arcade.ly/games/asteroids/&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the game’s menu eventually appears (which will take a while - possibly 10 seconds or more whilst the game’s JavaScript loads), click the &lt;strong&gt;Play&lt;/strong&gt; button. &lt;em&gt;(Btw, you may notice some JS errors in Adsense code whilst you do this; it seems that ads don’t necessarily play well with really slow connections.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Assuming you have both sound effects and music enabled you’ll probably find that the game plays fine, but there’s no music to start off with (and it takes quite some time before you hear any). It should also be obvious that some of the sounds are missing. As you carry on playing you’ll start to hear more and more of the different sound effects as they gradually load.&lt;/p&gt;
&lt;p&gt;So the end result is that, as much as the sound effects and music taken together are quite large assets, deferring their loading allows for a much more satisfactory experience for the user, because they can start playing the game straight away. This is despite the fact that complete loading of the game and all assets is measurably no faster than if I loaded them all upfront.&lt;/p&gt;
&lt;p&gt;In my next post I’ll talk about how to get that key above the fold content loaded and rendered as quickly as possible so that, even though there’s still quite a lot of work to do before the page is fully loaded and interactive, the user has the impression that content has loaded and that something is at least happening.&lt;/p&gt;
&lt;hr&gt;
&lt;h5 id=&quot;Read-the-rest-of-this-series-on-perceived-performance:&quot;&gt;Read the rest of this series on perceived performance:&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;PREVIOUS:&lt;/strong&gt; &lt;a href=&quot;https://arcade.ly/blog/2017/03/04/perceived-performance-part-1-what-is-perceived-performance/&quot;&gt;Perceived performance part 1: what is perceived performance?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;THIS POST:&lt;/strong&gt; &lt;a href=&quot;https://arcade.ly/blog/2017/03/06/perceived-performance-part-2-load-assets-asynchronously-or-on-demand/&quot;&gt;Perceived performance part 2: load assets asynchronously or on-demand&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;COMING SOON:&lt;/strong&gt; Perceived performance part 3: load key content first and give the user immediate feedback&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Perceived performance part 1: what is perceived performance?</title>
    <link href="https://arcade.ly/blog/2017/03/04/perceived-performance-part-1-what-is-perceived-performance/" />
    <updated>2017-03-04T10:04:23Z</updated>
    <id>https://arcade.ly/blog/2017/03/04/perceived-performance-part-1-what-is-perceived-performance/</id>
    <content type="html">&lt;p&gt;I have this talk I first gave late last year called &lt;a href=&quot;http://www.slideshare.net/bartread/client-side-performance-for-back-end-developers-camb-expert-talks-nov-2016&quot;&gt;Client-side performance for back-end developers&lt;/a&gt;, of which those slides are just the first iteration. A notable topic I haven’t included, is the importance of &lt;a href=&quot;https://en.wikipedia.org/wiki/Perceived_performance&quot;&gt;perceived performance&lt;/a&gt;. Yet it’s something I’m very conscious of, and I’ve been working continuously to improve it for &lt;a href=&quot;https://arcade.ly&quot;&gt;arcade.ly&lt;/a&gt;. My basic question is always, “How does it &lt;em&gt;feel&lt;/em&gt; to use this?” And this is a principle that cuts across the whole site, especially the games. I want it to feel great: I want it to feel slick, fast, responsive on all devices and browsers.&lt;/p&gt;
&lt;p&gt;Running across &lt;a href=&quot;https://www.keycdn.com/blog/perceived-performance&quot;&gt;this piece&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/brianleejackson&quot;&gt;Brian Jackson&lt;/a&gt; (formerly of &lt;a href=&quot;https://www.keycdn.com/&quot;&gt;keycdn&lt;/a&gt;) the other day brought the topic of perceived performance back to the forefront of my mind, and reminded me was something I should write about. So in this series of posts I want to cover what perceived performance is, and how I’ve improved it on &lt;a href=&quot;https://arcade.ly&quot;&gt;arcade.ly&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;OK, so let’s start off with perhaps the most important question: what &lt;em&gt;is&lt;/em&gt; perceived performance?&lt;/p&gt;
&lt;p&gt;Well, one way to look at it is it’s the answer to the question, “How fast does this &lt;em&gt;feel&lt;/em&gt;?” So, it’s not about how fast some process really is, but about how fast a user perceives it as being. And so it can sometimes be surprising when real, objective, measurable performance and perceived performance don’t always correlate. Now with that being said, it should be obvious that in many cases improving real performance can often help with the perception of performance too, but sometimes you can improve perceived performance significantly without improving real performance in any way (and sometimes whilst making it slightly worse).&lt;/p&gt;
&lt;p&gt;You might legitimately wonder why this matters but, when it comes to the web, the fact is that slower websites lead to dissatisfied visitors who don’t hang around, and fewer conversions (however &lt;em&gt;you&lt;/em&gt; define conversion for your site). And never mind how fast it is or isn’t, if your website even &lt;em&gt;feels&lt;/em&gt; slow, you’re going to lose out. People will vote with their attention by directing it elsewhere.&lt;/p&gt;
&lt;p&gt;The key point I’d like to make - and I hope this isn’t too controversial - is that your users’ perception of your web app’s performance is actually more important than its real, measured performance. Like I say, perceived performance is &lt;em&gt;key&lt;/em&gt; to a great user experience.&lt;/p&gt;
&lt;p&gt;How have I approached this for &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt;? Well the first thing to say is that I have, and continue to, expend effort on making everything actually, measurably, really faster, and these are things that I’ll talk about in other posts. Beyond that, and it’s really something you do when you’re out of options for making something measurably faster, I’ve used a few tricks and techniques to improve perception:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Load assets resources out of band/asynchronously or on demand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Load key above the fold content as quickly as possible, and give the user immediate feedback that something is happening.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Gracefully degrade the experience on slower devices to keep performance up.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Load and render key above the fold content first, delay everything else, even if this means the page load takes multiple seconds overall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m going to talk about each of these techniques in more detail in the follow-up posts, so you can see exactly what I’ve done. I’ll also highlight areas where improvement is still needed (I’m nothing if not honest!).&lt;/p&gt;
&lt;p&gt;For now though, I’m just going to leave you with this take home…&lt;/p&gt;
&lt;blockquote class=&quot;blockquote-reverse&quot;&gt;
_**PRINCIPLE: Improving real performance will generally improve users&#39; perception of performance. Where real performance improvements are not possible, selectively loading and displaying content, or otherwise indicating that activity is taking place, can improve their perception of performance even at a small cost to real performance.**_
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h5 id=&quot;Read-the-rest-of-this-series-on-perceived-performance:&quot;&gt;Read the rest of this series on perceived performance:&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;THIS POST:&lt;/strong&gt; &lt;a href=&quot;https://arcade.ly/blog/2017/03/04/perceived-performance-part-1-what-is-perceived-performance/&quot;&gt;Perceived performance part 1: what is perceived performance?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NEXT:&lt;/strong&gt; &lt;a href=&quot;https://arcade.ly/blog/2017/03/06/perceived-performance-part-2-load-assets-asynchronously-or-on-demand/&quot;&gt;Perceived performance part 2: load assets asynchronously or on-demand&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;COMING SOON:&lt;/strong&gt; Perceived performance part 3: load key content first and give the user immediate feedback&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Is that Star Castle in The Expanse on Netflix?</title>
    <link href="https://arcade.ly/blog/2017/02/09/Is-that-star-castle-in-the-expanse/" />
    <updated>2017-02-09T19:15:56Z</updated>
    <id>https://arcade.ly/blog/2017/02/09/Is-that-star-castle-in-the-expanse/</id>
    <content type="html">&lt;p&gt;A few days ago I started watching the excellent new series, &lt;a href=&quot;https://www.netflix.com/title/80029822&quot;&gt;The Expanse&lt;/a&gt;, on Netflix. They claim it as a Netflix Original, but both &lt;a href=&quot;http://www.imdb.com/title/tt3230854/&quot;&gt;IMDB&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Expanse_%28TV_series%29&quot;&gt;Wikipedia&lt;/a&gt; claim it’s an Alcon Entertainment and Sean Daniel Company production, originally for SyFy, so I don’t know.&lt;/p&gt;
&lt;p&gt;Anyway, I don’t want to say too much about it for fear of revealing spoilers. Suffice to say back in the day I was a big fan of series like &lt;a href=&quot;http://www.imdb.com/title/tt0213338/&quot;&gt;Cowboy Bebop&lt;/a&gt;, &lt;a href=&quot;http://www.imdb.com/title/tt0303461/&quot;&gt;Firefly&lt;/a&gt; (and, of course, &lt;a href=&quot;http://www.imdb.com/title/tt0379786/&quot;&gt;Serenity&lt;/a&gt;), and &lt;a href=&quot;http://www.imdb.com/title/tt0407362/&quot;&gt;Battlestar Galactica&lt;/a&gt;, and The Expanse goes some way toward scratching that itch in a way that hasn’t really happened since BSG ended. If you’re a fan of sci-fi or space opera with a bit of an edge to it I’d highly recommend you check it out.&lt;/p&gt;
&lt;p&gt;Anyway, I’ve been watching this in the evenings for a few days, and towards the end of episode 8 I spotted something interesting on a holo-display. Here’s the shot with enough of the dialogue to set the scene:&lt;/p&gt;
&lt;aside class=&quot;indented&quot;&gt;
_**Johnson:** You said you cracked it. Hope it&#39;s not a letter to a wife._
&lt;/aside&gt;
&lt;aside class=&quot;indented&quot;&gt;
_**Officer:** It&#39;s not: it&#39;s a real-time tactical record of the Donnager battle right down to the millisecond with full sensor logs and analysis._
&lt;/aside&gt;
&lt;br&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/star-castle-in-the-expanse-tv-show.jpg&quot; alt=&quot;Do we think this officer is actually playing Star Castle in The Expanse and not, as she claims, analysing a space battle involving the Donnager? Hmm.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Oh, really?&lt;/p&gt;
&lt;p&gt;Well, you can call it whatever you like but it looks to me like your boss just busted you enjoying a &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;good old fashioned game of Star Castle&lt;/a&gt; when you should have been working.&lt;/p&gt;
&lt;p&gt;Still, it was probably worth it, don’t you think? And if you &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;fancy a game yourself just click here&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How good is Cloudflare&#39;s Always Online™ technology?</title>
    <link href="https://arcade.ly/blog/2017/01/22/how-good-is-cloudflare-always-online/" />
    <updated>2017-01-22T19:50:18Z</updated>
    <id>https://arcade.ly/blog/2017/01/22/how-good-is-cloudflare-always-online/</id>
    <content type="html">&lt;p&gt;If your site goes down, exactly how good is &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare’s&lt;/a&gt; &lt;a href=&quot;https://www.cloudflare.com/always-online/&quot;&gt;Always Online™&lt;/a&gt; technology at bailing you out?&lt;/p&gt;
&lt;p&gt;Rather good. But…&lt;/p&gt;
&lt;p&gt;We’ll get to the “but” in a minute. Let’s deal with the “rather good” first. Here’s &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt;’s homepage when the site is down:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/cloudflarealwaysonline.jpg&quot; alt=&quot;The arcade.ly homepage when the site is down. Note that Cloudflare is still serving it up, which is great.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;By down, in this case I mean the service that runs the site is down, although the instance it normally runs on is up.&lt;/p&gt;
&lt;p&gt;Now apart from that banner, which is basically an ad that says, “Cloudflare is awesome and you should use it!”, everything looks normal. I could probably get rid of that by giving Cloudflare more money, and maybe one day I will. Nevertheless, my dodgy web design skills aside (and this page &lt;strong&gt;is&lt;/strong&gt; going to get a makeover), everything that should be on the homepage has loaded and is working fine because Cloudflare has been able to serve it up from its cache.&lt;/p&gt;
&lt;p&gt;So can I play the games?&lt;/p&gt;
&lt;p&gt;Yes, I can. My &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Asteroids&lt;/a&gt; remake, &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Shoot the Rocks&lt;/a&gt;, works fine. Here you can see it loading as if nothing is amiss:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/clfalwaysonline-asteroids-shoot-the-rocks-loading.jpg&quot; alt=&quot;Shoot the Rocks loading like nothing&#39;s wrong - awesome.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;The main menu looks a bit gimpy because the banner messes with the layout a bit, which might be more problematic on iOS than on desktop or Android, but everything still works:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/clfalwaysonline-asteroids-shoot-the-rocks-menu-borked.jpg&quot; alt=&quot;The Shoot the Rocks menu is slightly messed up but still works.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;And the game itself runs flawlessly, and weirdly without the banner:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/clfalwaysonline-asteroids-shoot-the-rocks-flawless.jpg&quot; alt=&quot;Shoot the Rocks running flawlessly, thanks to Cloudflare.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Well, bless Cloudflare is all I can say to that.&lt;/p&gt;
&lt;p&gt;There was that “but” though, so we should take a look at that because it doesn’t always work out so well. Here’s what happened when I tried to run &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt;, my take on &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Castle&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/clfalwaysonline-star-castle-star-citadel-borked.jpg&quot; alt=&quot;Argh! Star Citadel doesn&#39;t work - I just get this nasty error.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;That’s a less happy, although completely understandable, outcome: Cloudflare simply didn’t have the page in its cache. If your site was up for a while before it went down, or you get a decent amount of traffic around the world, hopefully this doesn’t happen.&lt;/p&gt;
&lt;p&gt;I say all around the world because, being a CDN, Cloudflare can cache your site on edge servers all over the globe but won’t cache it anywhere until somebody accesses it. This means that pages will be cached on servers located geographically nearer to people who use them (in general), but not on servers located geographically further away.&lt;/p&gt;
&lt;p&gt;Thus in places where your site gets used a lot, hopefully people get to carry on using it even whilst your back end services or infrastructure are burning, but people in other areas won’t get the same experience, and will instead see an error.&lt;/p&gt;
&lt;p&gt;Again, if I paid more I think could probably replace the above with some sort of branded custom error page, so choose an appropriate plan for your use case.&lt;/p&gt;
&lt;h3 id=&quot;What-will-and-won’t-work-offline?&quot;&gt;What will and won’t work offline?&lt;/h3&gt;
&lt;p&gt;So this all works because of the way I’ve architected the site. Almost everything you see when you visit a page or play a game on arcade.ly is static content served up by my web server or, more likely, Cloudflare. As little as possible happens on the server, which is why Cloudflare can bail me out.&lt;/p&gt;
&lt;p&gt;If all my content were dynamically generated server side it would be a different story: I might still see a static version of the pages as last seen by Cloudflare, but &lt;strong&gt;nothing&lt;/strong&gt; would really work.&lt;/p&gt;
&lt;p&gt;In reality most sites sit somewhere in between these two extremes, and even for arcade.ly, there are things that wouldn’t work.&lt;/p&gt;
&lt;p&gt;For example, the as yet publicly unavailable hiscore service where you can record your performance in each game for posterity and bragging rights to your friends? Totally not going to work because there’s back end infrastructure in there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A service to record high scores - in fact ideally I’d record scores from all games and build up a gigantic league table for each game, as played in modern and classic modes, split by device, and so on,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A data store to put all of this in: right now I’m thinking SQL Azure because we’re talking about a big table of data, for which SQL is absolutely perfect (this definitely is &lt;strong&gt;not&lt;/strong&gt; a document shaped problem at any rate),&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A service to expose that hiscore data, or a league table if you like (or possibly we’re talking about two different views on the same data), to clients, which might be the games themselves, or other stats pages on the site, possibly in the future even dedicated mobile apps.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the two services are deployed separately to the main website they might still be available but, if not, or if there’s a problem with the hosting infrastructure not in a million years will the above work.&lt;/p&gt;
&lt;p&gt;That doesn’t mean you can’t record a player’s score in the meantime. There is, of course, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage&quot;&gt;LocalStorage&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/IndexedDB_API&quot;&gt;IndexedDB&lt;/a&gt;, so if you’ve built your site for an offline first experience, your user’s might not even notice that there’s a problem, particularly if hiscore data was loaded before the site/service went down. You can just record the player’s score and submit it later when the site is back online.&lt;/p&gt;
&lt;p&gt;There are, of course, caveats. Any data you put into LocalStorage as plain text is incredibly easy to tamper with using tools such as the &lt;a href=&quot;https://chrome.google.com/webstore/detail/html5-storage-manager-all/giompennnhheakjcnobejbnjgbbkmdnd?hl=en&quot;&gt;HTML5 Storage Manager All In One&lt;/a&gt; Chrome extension. You can use a library like &lt;a href=&quot;http://pieroxy.net/blog/pages/lz-string/index.html&quot;&gt;lz-string&lt;/a&gt; to compress any data you add to LocalStorage to render it no longer human-readable, but somebody who’s committed can always copy and paste it out, decompress, edit, recompress, and then paste it back in to LocalStorage. How much you’re worried about that is something you should consider.&lt;/p&gt;
&lt;p&gt;You can also encrypt the data using, for example, this &lt;a href=&quot;http://www.movable-type.co.uk/scripts/aes.html&quot;&gt;JavaScript AES implementation&lt;/a&gt;. This will certainly discourage the casual hacker, but remember that the &lt;a href=&quot;https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/&quot;&gt;merits of cryptography in JavaScript are extremely questionable&lt;/a&gt; (see also &lt;a href=&quot;https://tonyarcieri.com/whats-wrong-with-webcrypto&quot;&gt;here&lt;/a&gt; for a slightly more recent perspective). JS encryption certainly isn’t going to stop somebody who’s committed to tampering with the data.&lt;/p&gt;
&lt;p&gt;As far as I know there’s no (supported) mechanism for accessing IndexedDB except by the site that wrote the data but, remember, this is data stored on the client so you may want to consider it suspect anyway.&lt;/p&gt;
&lt;p&gt;LocalStorage and IndexedDB work well when whatever you store in them will eventually undergo strong validation on the server-side. It does not work so well when this isn’t the case - perhaps because the data is collected or generated in such a way that validation is very hard - and you can’t trust your users (because they want a ludicrously high score for bragging rights, for example).&lt;/p&gt;
&lt;p&gt;Anyway, to drag this back to the point: you &lt;strong&gt;can&lt;/strong&gt; offer a decent offline experience, and this will prove especially useful in scenarios where strong validation later on, when your web client can once again connect to your services, is suitable.&lt;/p&gt;
&lt;p&gt;In some situations you’re just going to have to tell the user that certain functionality isn’t available at the moment but it will be back online soon. For example, I’d like to offer some sort of player history and stats service. I’d really like to offer people the ability to compare with their friends, and perhaps compete directly with them. Clearly, this won’t work offline, but graceful failure is an option I’ll happily embrace.&lt;/p&gt;
&lt;p&gt;So, the verdict on Cloudflare Always Online™? Pretty good - just be aware of the limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Consider where your users are: will they be able to access the pages they need? Can you use a service such as &lt;a href=&quot;https://www.pingdom.com/&quot;&gt;pingdom&lt;/a&gt; to ensure Cloudflare caches the necessary pages? (Actually, thinking about it, maybe this is something Cloudflare already offer in their Business or Enterprise plans.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Consider how you can build an offline first experience for functionality with online requirements: can you hold data for later? Or perhaps limit functionality? Or can you at least make it unavailable gracefully by telling the user what’s going on, rather than just breaking?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that’s it. As always, hope this was useful, and please do leave questions or comments below.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>How to run a Node.js app as a service on Ubuntu</title>
    <link href="https://arcade.ly/blog/2017/01/21/running-a-nodejs-app-as-a-service-on-ubuntu/" />
    <updated>2017-01-21T21:07:52Z</updated>
    <id>https://arcade.ly/blog/2017/01/21/running-a-nodejs-app-as-a-service-on-ubuntu/</id>
    <content type="html">&lt;p&gt;One of the things I’ve needed to do with &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; for a while is start running node as a service.&lt;/p&gt;
&lt;p&gt;It’s easy enough to run background tasks directly from bash with a command like:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nohup&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; index.js &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&gt;&lt;/span&gt; /dev/null &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you run that command and log out of your ssh session, node will carry on running just fine in the background. But what happens if node falls over? For arcade.ly this has never happened, but that’s likely because the site hasn’t been under much stress up to now.&lt;/p&gt;
&lt;p&gt;(By the way, if you want a detailed discussion of running bash commands in the background it’s definitely worth checking out &lt;a href=&quot;https://www.maketecheasier.com/run-bash-commands-background-linux/&quot;&gt;Running Bash Commands in the Background the Right Way&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Realistically at some point the site is going to go down, and whilst &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare&lt;/a&gt; will to some extent bail me out, I’d like it to come back up again without a manual intervention, especially if it’s due to something as simple as the node process crapping out.&lt;/p&gt;
&lt;p&gt;Here you can see the effect of Cloudflare’s assistance. It’s not bad but that banner (basically an advert) at the top isn’t ideal. I suspect I could get rid of that by paying for a more expensive plan.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://arcade.ly/images/blog/cloudflarealwaysonline.jpg&quot; alt=&quot;Here&#39;s Cloudflare dragging my backside out of the fire when I&#39;m testing what happens when node goes down. It&#39;s not ideal, but at least you can still play the games.&quot;&gt;&lt;/figure&gt;
&lt;p&gt;The great thing is that you can still play the games so if you need a fix of &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Asteroids&lt;/a&gt; or &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Castle&lt;/a&gt; you can go ahead and fire up &lt;a href=&quot;https://arcade.ly/games/asteroids/&quot;&gt;Shoot the Rocks&lt;/a&gt; or &lt;a href=&quot;https://arcade.ly/games/starcastle/&quot;&gt;Star Citadel&lt;/a&gt; and they’ll (probably) still run. (I’ll blog on this topic in due course as well.)&lt;/p&gt;
&lt;p&gt;So how do we make sure it runs as a service and recovers without intervention?&lt;/p&gt;
&lt;p&gt;I’m running the site on an instance spun up from a bog standard Ubuntu AMI in EC2, and deploying is just a case of cloning my git repo into a new instance and running a script. Nothing fancy either: just a shell script that installs a few things, runs gulp, and copies some files around. (That’s right: no Docker. I’ll post about why later.)&lt;/p&gt;
&lt;p&gt;These days I’m much more familiar with Windows than Linux, so had to do a bit of digging around on how to run a process as a service, but it turns out to be quite straightforward, albeit that there are a couple of gotchas, and there are two different ways of doing it, depending upon which version of Ubuntu you’re using.&lt;/p&gt;
&lt;h2&gt;Pre Ubuntu 15.04 - upstart&lt;/h2&gt;
&lt;p&gt;Due to the inevitable lag as Google’s ranking algorithm catches up with today’s technological landscape the material you’ll most likely run across first suggests going down the upstart route, which involves writing a .conf file into /etc/init. However, whilst that’s fine if you’re running an older version of Ubuntu - and as of this writing I believe Ubuntu 14 is still widespread - upstart was deprecated in 15.04 in favour of systemd and if, like me, you’re running 16.x, well, you’re out of luck. If you’ve been keeping more up to date with Ubuntu developments than I have you probably already know about this.&lt;/p&gt;
&lt;p&gt;In theory you can still use &lt;code&gt;apt-get&lt;/code&gt; to install upstart on later versions of Ubuntu but this isn’t something I’d recommend due to reports of &lt;a href=&quot;http://askubuntu.com/questions/760615/ubuntu-16-04lts-does-not-boot-after-package-upstart-sysv-is-installed&quot;&gt;serious problems after doing so&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Still, if you are in the old world you can use upstart, and I’d recommend &lt;a href=&quot;https://gist.github.com/jobsamuel/6d6095d52228461f3c53&quot;&gt;this gist from Jobsamuel Núñez as a guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I followed these instructions and the only thing I did differently was to redirect to &lt;code&gt;/dev/null&lt;/code&gt; because all my logging is sent to &lt;a href=&quot;https://www.loggly.com/&quot;&gt;loggly&lt;/a&gt; because I don’t want to manually manage log files. You can see this under the &lt;code&gt;script&lt;/code&gt; section here:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;description &amp;quot;Arcade.ly node.js server&amp;quot;
author      &amp;quot;Bart Read&amp;quot;

# used to be: start on startup
# until we found some mounts weren&#39;t ready yet while booting

start on started mountall
stop on shutdown

# automatically respawn

respawn
respawn limit 99 5

script
    
    export HOME=&amp;quot;/root&amp;quot;
    exec /usr/bin/node /home/ubuntu/arcade/build/production/index.js &amp;gt;&amp;gt; /dev/null 2&amp;gt;&amp;amp;1

end script

post-start script
   
   # optionally put a script here that will notifiy you node has (re)started
   # /root/bin/hoptoad.sh &amp;quot;node.js has started!&amp;quot;
   
end script
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the respawn settings. This tells upstart to try and respawn the process up to 99 times at 5 second intervals. If the service can’t be restarted after 99 tries, upstart will give up.&lt;/p&gt;
&lt;h2&gt;Ubuntu 15.04 and later - systemd&lt;/h2&gt;
&lt;p&gt;This is no more difficult that using upstart. In fact, in some ways it’s a bit easier. Full instructions at https://www.axllent.org/docs/view/nodejs-service-with-systemd/. Just skip over the node server section at the top and start reading the &lt;b&gt;systemd&lt;/b&gt; section. I had only two issues.&lt;/p&gt;
&lt;p&gt;The first was simple: I needed to prefix all the &lt;code&gt;systemctl&lt;/code&gt; commands with &lt;code&gt;sudo&lt;/code&gt;, otherwise it would ask me for a password, which I don’t have because I log in with ssh. Here’s an example:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; arcadely.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second was that when I first started the service, whilst all my 301 permanent redirects were working fine, none of the site content would load - everything was coming back with 404 - Not Found. This turned out to be because the working directory of the service process is set to &lt;code&gt;/&lt;/code&gt; by default, and thus when express.js tried to serve up content relative to this folder it inevitably failed. This is easily fixed by setting the &lt;code&gt;WorkingDirectory&lt;/code&gt; property in the descriptor, as you can see here:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;Arcade.ly Node.js Server&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#Requires=After=mysql.service       # Requires the mysql service to run first&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/usr/bin/node /home/ubuntu/arcade/build/production/index.js&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;always&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;RestartSec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;10                       # Restart service after 10 seconds if node service crashes&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;StandardOutput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;syslog               # Output to syslog&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;syslog                # Output to syslog&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;SyslogIdentifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;arcadely-nodejs-server&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#User=&amp;lt;alternate user&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#Group=&amp;lt;alternate group&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#Environment=NODE_ENV=production PORT=1337&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;WorkingDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;/home/ubuntu/arcade/build/production&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value attr-value&quot;&gt;multi-user.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note also the restart settings - this is what tells systemd to bring the service back up again if it fails.&lt;/p&gt;
&lt;p&gt;The following bash fragment shows how I’m installing the service.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#! /bin/sh&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Other deployment actions here. Want to install (or bounce) service last.&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /etc/systemd/system/arcadely.service &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl stop arcadely.service
    &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl disable arcadely.service
&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;cp &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; ./arcadely.service /etc/systemd/system/arcadely.service
&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; arcadely.service
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start arcadely.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that after I’ve installed it once, I don’t bother installing it again (which is just a file copy), but I do bounce it so that node picks up any changes that have just been deployed. I could use &lt;a href=&quot;https://nodemon.io/&quot;&gt;nodemon&lt;/a&gt; for this, and not bother with the bounce, but I’ve found that sometimes nodemon can sometimes get stuck and require manual intervention if the app it’s monitoring crashes due to restarting in the midst of a deployment, which can easily happen if the deployment takes longer than usual, for some reason. I do still use nodemon during development, in concert with a &lt;code&gt;gulp watch&lt;/code&gt; task.&lt;/p&gt;
&lt;p&gt;I might change this so that it copies the service descriptor every time, in case there are changes. This is likely to be infrequent but it means I never have to think about it again, which is a bonus.&lt;/p&gt;
&lt;p&gt;The only thing is, when the service descriptor changes you generally need to do a reload of the &lt;code&gt;systemctl&lt;/code&gt; daemon:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t worry - if this is necessary you’ll be prompted. There’s no guesswork. In any case, I’ll probably automate this away by adding it to the script.&lt;/p&gt;
&lt;p&gt;Anyway, I’m now in a position where I can reliably deploy to a vanilla Ubuntu AMI with all dependencies in just a couple of minutes. This includes time to compile Python 2.7.13, which is the lengthiest operation.&lt;/p&gt;
&lt;p&gt;I hope the above tips come in useful!&lt;/p&gt;
&lt;h5 id=&quot;EDIT---24-Jan-2017:-systemd-local-root-exploit-in-v228&quot;&gt;EDIT - 24 Jan 2017: systemd local root exploit in v228&lt;/h5&gt;
&lt;p&gt;There’s been some noise today about a local root exploit in systemd v228. I first picked this up on &lt;a href=&quot;https://news.ycombinator.com/item?id=13469935&quot;&gt;HackerNews&lt;/a&gt;, and you can find the original report &lt;a href=&quot;http://www.openwall.com/lists/oss-security/2017/01/24/4&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This has been fixed in later releases (and note the controversy about the stealthy way that happened), but you can check if your systems are vulnerable using one of the following commands to check your systemd version. Which command you use depends on which distro you’re using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;init --version&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;systemd --version&lt;/code&gt; (Ubuntu, at least v16.04; Debian Jessie; Mint 18)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;systemctl --version&lt;/code&gt; (Fedora)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, here’s the output for arcade.ly’s production instance:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ubuntu@&lt;span class=&quot;token environment constant&quot;&gt;HOSTNAME&lt;/span&gt;:~$ systemd &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;
systemd &lt;span class=&quot;token number&quot;&gt;229&lt;/span&gt;
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ &lt;span class=&quot;token parameter variable&quot;&gt;-LZ4&lt;/span&gt; +SECCOMP +BLKID +ELFUTILS +KMOD &lt;span class=&quot;token parameter variable&quot;&gt;-IDN&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If in doubt just try them all until you find the one that works.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Welcome to the arcade.ly blog</title>
    <link href="https://arcade.ly/blog/2017/01/20/hello-world/" />
    <updated>2017-01-20T21:00:00Z</updated>
    <id>https://arcade.ly/blog/2017/01/20/hello-world/</id>
    <content type="html">&lt;p&gt;Welcome to the &lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; blog, where we’ll be talking about games, game dev, and web performance.&lt;/p&gt;
&lt;p&gt;All our games are built with HTML5, JavaScript, and CSS. That’s right: no Flash. Which means they work on mobile and tablet, as well as desktop and laptop.&lt;/p&gt;
&lt;p&gt;So far we only have a couple of games:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://arcade.ly/games/asteroids&quot;&gt;Shoot the Rocks!&lt;/a&gt; - our take on the 1979 arcade classic, &lt;a href=&quot;https://en.wikipedia.org/wiki/Asteroids_(video_game)&quot;&gt;Asteroids&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://arcade.ly/games/starcastle&quot;&gt;Star Citadel&lt;/a&gt; - an homage to another vector classic from 1980, this time Cinematronics’ &lt;a href=&quot;https://en.wikipedia.org/wiki/Star_Castle&quot;&gt;Star Castle&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We hope you enjoy playing them!&lt;/p&gt;
&lt;p&gt;For anyone who’s interested, our behind the scenes tech stack is &lt;a href=&quot;http://expressjs.com/&quot;&gt;express.js&lt;/a&gt; and &lt;a href=&quot;https://nodejs.org/en/&quot;&gt;node.js&lt;/a&gt; running on &lt;a href=&quot;https://bitnami.com/&quot;&gt;Bitnami&lt;/a&gt;, currently in &lt;a href=&quot;https://aws.amazon.com/&quot;&gt;Amazon Web Services’&lt;/a&gt; with &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare&lt;/a&gt; as CDN. The blog is generated by &lt;a href=&quot;https://hexo.io/&quot;&gt;Hexo&lt;/a&gt; using the excellent &lt;a href=&quot;https://github.com/klugjo/hexo-theme-alpha-dust&quot;&gt;alpha-dust&lt;/a&gt; theme from &lt;a href=&quot;http://www.codeblocq.com/&quot;&gt;Jonathan Klughertz&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We recommend you subscribe to our Atom feed for posts and updates on new game releases.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Licensing and attributions</title>
    <link href="https://arcade.ly/blog/2017/01/01/licensing-and-attributions/" />
    <updated>2017-01-01T00:00:02Z</updated>
    <id>https://arcade.ly/blog/2017/01/01/licensing-and-attributions/</id>
    <content type="html">&lt;section id=&quot;blogPostNavigation&quot;&gt;
&lt;h2 id=&quot;Contents&quot;&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#Contents&quot;&gt;Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#Licensing-arcade.ly-games&quot;&gt;Licensing arcade.ly games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#Credits-and-attributions&quot;&gt;Credits and attributions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#For-all-games&quot;&gt;For all games&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#Star-Citadel&quot;&gt;Star Citadel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/licensing-and-attributions/#Shoot-The-Rocks&quot;&gt;Shoot The Rocks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;h2 id=&quot;Licensing-arcade.ly-games&quot;&gt;Licensing arcade.ly games&lt;/h2&gt;
&lt;p&gt;You &lt;strong&gt;MUST&lt;/strong&gt; license the use of arcade.ly games if you wish to do any of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Host, embed, or otherwise include any arcade.ly games in a website other than arcade.ly,&lt;br&gt;
Provide access to arcade.ly’s games in any public venue or as part of any commercial enterprise,&lt;/li&gt;
&lt;li&gt;Build or offer any product or service for sale that includes any arcade.ly games,&lt;/li&gt;
&lt;li&gt;Use arcade.ly games or footage of arcade.ly games in any kind of media or presentation intended for consumption by anyone other than yourself,&lt;/li&gt;
&lt;li&gt;Use arcade.ly games in any other commercial context.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You &lt;strong&gt;MUST&lt;/strong&gt; also get permission for any public, party, or group use even in a non-commercial context.&lt;/p&gt;
&lt;p&gt;Please contact &lt;a href=&quot;&amp;#109;&amp;#x61;&amp;#105;&amp;#x6c;&amp;#116;&amp;#x6f;&amp;#58;&amp;#x77;&amp;#101;&amp;#x62;&amp;#109;&amp;#x61;&amp;#115;&amp;#x74;&amp;#101;&amp;#x72;&amp;#64;&amp;#x61;&amp;#114;&amp;#x63;&amp;#97;&amp;#x64;&amp;#101;&amp;#x2e;&amp;#108;&amp;#x79;&quot;&gt;the webmaster&lt;/a&gt; with a brief description of how you intend to use the games for more information.&lt;/p&gt;
&lt;h2 id=&quot;Credits-and-attributions&quot;&gt;Credits and attributions&lt;/h2&gt;
&lt;h3 id=&quot;For-all-games&quot;&gt;For all games&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Game Design, Graphics, Sound Effects, Programming and Infrastructure&lt;/strong&gt; - Bart Read&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Additional Sound Effects&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freesound.org/people/cbakos/sounds/50635/&quot;&gt;NewWallSwitchON&lt;/a&gt; by cbakos used under the terms of the &lt;a href=&quot;http://creativecommons.org/licenses/by/3.0/legalcode&quot;&gt;Creative Commons Attribution License&lt;/a&gt;. The original sound effect from Freesound is a WAV: for the purpose of browser compatibility arcade.ly has re-encoded it as an MP3.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hiscore name filtering&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Uses the &lt;a href=&quot;https://github.com/LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words&quot;&gt;List of Dirty, Naughty, Obscene, and Otherwise Bad Words&lt;/a&gt; by Shutterstock and its contributors, used under the terms of the &lt;a href=&quot;https://creativecommons.org/licenses/by/4.0/&quot;&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. arcade.ly uses a subset of its languages, leaves out some terms that are ordinary words in other languages, and adds lists of its own.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Star-Citadel&quot;&gt;Star Citadel&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Music&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title Music:&lt;/strong&gt; &lt;a href=&quot;http://www.premiumbeat.com/royalty_free_music/songs/arrested-for-driving-while-mad&quot;&gt;Arrested for Driving While Mad&lt;/a&gt; by Francesco D’Andrea, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In Game Music:&lt;/strong&gt; &lt;a href=&quot;http://www.premiumbeat.com/royalty_free_music/songs/a-naked-gun-bank-assault&quot;&gt;A Naked Gun: Bank Assault!&lt;/a&gt; by Francesco D’Andrea, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Game Over Music:&lt;/strong&gt; &lt;a href=&quot;http://www.premiumbeat.com/royalty_free_music/songs/good-ol-western&quot;&gt;Good Ol’ Western&lt;/a&gt; by Haim Mazar, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;Shoot-The-Rocks&quot;&gt;Shoot The Rocks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Music&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Title Music:&lt;/strong&gt; &lt;a href=&quot;https://www.premiumbeat.com/royalty-free-tracks/renegade&quot;&gt;Renegade&lt;/a&gt; by Gyom, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In Game Music:&lt;/strong&gt; &lt;a href=&quot;https://www.premiumbeat.com/royalty-free-tracks/ready-to-strike&quot;&gt;Ready to Strike&lt;/a&gt; by Brightside Studio, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Game Over Music:&lt;/strong&gt; &lt;a href=&quot;https://www.premiumbeat.com/royalty-free-tracks/lonely-days&quot;&gt;Lonely Days&lt;/a&gt; by The Commandeers, licensed from &lt;a href=&quot;http://www.premiumbeat.com/&quot;&gt;PremiumBeat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Privacy and compliance</title>
    <link href="https://arcade.ly/blog/2017/01/01/terms-and-conditions/" />
    <updated>2017-01-01T00:00:01Z</updated>
    <id>https://arcade.ly/blog/2017/01/01/terms-and-conditions/</id>
    <content type="html">&lt;section id=&quot;blogPostNavigation&quot;&gt;
&lt;h2 id=&quot;Contents&quot;&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Contents&quot;&gt;Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Privacy-notice&quot;&gt;Privacy notice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#How-arcade.ly-uses-your-information&quot;&gt;How arcade.ly uses your information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Third-parties-and-their-sites&quot;&gt;Third parties and their sites&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Points-covering-all-third-parties&quot;&gt;Points covering all third parties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#How-arcade.ly-uses-analytics&quot;&gt;How arcade.ly uses analytics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Specifics-of-handling-log-files-and-IP-addresses&quot;&gt;Specifics of handling log files and IP addresses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Non-personal-information&quot;&gt;Non personal information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Cookies&quot;&gt;Cookies&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Introduction-to-cookies&quot;&gt;Introduction to cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#How-arcade.ly-uses-essential-cookies&quot;&gt;How arcade.ly uses essential cookies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Your-rights&quot;&gt;Your rights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Complaints-and-queries&quot;&gt;Complaints and queries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Reporting-security-issues&quot;&gt;Reporting security issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Changes-to-this-privacy-policy&quot;&gt;Changes to this privacy policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#Questions-about-data-privacy&quot;&gt;Questions about data privacy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;h2 id=&quot;Privacy-notice&quot;&gt;Privacy notice&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://arcade.ly/&quot;&gt;arcade.ly&lt;/a&gt; respects your privacy. All information you give us is held with the utmost care and security.&lt;/p&gt;
&lt;p&gt;Please take time to review this privacy policy as it sets out our privacy practices and tells you how your personal information will be treated by us. We do not sell, rent or loan any identifiable information regarding our users or customers to any third party. Only in the circumstances described within this Privacy Policy would we share your information with any third party.&lt;/p&gt;
&lt;p&gt;Our use of personal data is in accordance with the General Data Protection Regulations 2016 (GDPR).&lt;/p&gt;
&lt;h2 id=&quot;How-arcade.ly-uses-your-information&quot;&gt;How arcade.ly uses your information&lt;/h2&gt;
&lt;p&gt;arcade.ly will not collect any personal information about individuals except as set out in this privacy policy.&lt;/p&gt;
&lt;p&gt;When you play our games, the personal information we collect and keep with each score you submit is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the game you played and your score&lt;/li&gt;
&lt;li&gt;the name shown alongside your score: either the randomly generated nickname we assign to your session or, if you choose one, a name of your own (we always keep the generated nickname with the score, even if you change the name shown)&lt;/li&gt;
&lt;li&gt;the country you submitted the score from, as determined from your IP address by our content delivery network, Cloudflare - we store only the two-letter country code with your score, never your IP address itself&lt;/li&gt;
&lt;li&gt;the browser and device you were playing on (your browser’s user agent string and platform)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our hiscore tables are public: anyone can see the name, score, country flag, type of device (computer, phone, or tablet), and date of each score in them. Because they’re public, names are checked automatically for profanity (in English and many European languages): a name that fails the check is replaced with your generated nickname, and we may hide any hiscore whose name is offensive.&lt;/p&gt;
&lt;p&gt;We will use the personal information provided to us in order to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;present you with information regarding games you’ve played, such as hiscore tables for each game&lt;/li&gt;
&lt;li&gt;monitor usage patterns, errors and performance of the site across different geographies, browsers and form factors to help us diagnose problems with the site and make improvements that offer you a better experience&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Certain information is collected automatically, as described later in this privacy policy.&lt;/p&gt;
&lt;h2 id=&quot;Third-parties-and-their-sites&quot;&gt;Third-parties and their sites&lt;/h2&gt;
&lt;h3 id=&quot;Points-covering-all-third-parties&quot;&gt;Points covering all third parties&lt;/h3&gt;
&lt;p&gt;arcade.ly is served through Cloudflare’s content delivery network, which, as part of handling each request, tells us the country it came from - see “Specifics of handling log files and IP addresses”, below, for how we use that.&lt;/p&gt;
&lt;p&gt;Where our services make use of hosted tools, hosted data storage and payment services provided by third parties any of your personal information stored by such third parties shall be subject to their privacy provisions.&lt;/p&gt;
&lt;p&gt;Whilst we choose our business partners carefully, unfortunately we cannot be responsible for their privacy policies and practices or those of other sites that you access using links from our site. Therefore we recommend that you check the policy of each site you visit.&lt;/p&gt;
&lt;p&gt;The following is a list of links to the privacy policies of partner sites and services we use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GoatCounter: https://www.goatcounter.com/help/privacy&lt;/li&gt;
&lt;li&gt;Loggly: https://www.loggly.com/about/privacy-policy/&lt;/li&gt;
&lt;li&gt;Cloudflare: https://www.cloudflare.com/privacypolicy/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, if you linked to arcade.ly from a third party site, we cannot be responsible for the privacy policies and practices of the owners or operators of that third party site and recommend that you check the policy of that third party site and contact its owner or operator if you have any concerns or questions.&lt;/p&gt;
&lt;h3 id=&quot;How-arcade.ly-uses-analytics&quot;&gt;How arcade.ly uses analytics&lt;/h3&gt;
&lt;p&gt;arcade.ly uses &lt;a href=&quot;https://www.goatcounter.com/&quot;&gt;GoatCounter&lt;/a&gt; to monitor usage of the site and games, replacing the Google Analytics service we used previously. We use the data it collects to improve your experience, and to help diagnose and fix user experience issues and bugs.&lt;/p&gt;
&lt;p&gt;GoatCounter is designed with privacy in mind: per its own published privacy policy, it doesn’t use cookies to track you, and it doesn’t store your IP address, full User-Agent header, or any other tracker ID. GoatCounter’s own position is that it “probably doesn’t require a GDPR consent notice” for this reason, though this isn’t a legal conclusion we’re making ourselves - see their privacy policy, linked above, for the full detail.&lt;/p&gt;
&lt;p&gt;We also use GoatCounter’s event-tracking feature to record specific interactions within our games (e.g. starting a game, completing a level, submitting a hiscore) so that we can understand whether players are able to play and complete our games properly.&lt;/p&gt;
&lt;p&gt;Our &lt;a href=&quot;https://arcade.ly/mcp-server&quot;&gt;MCP server&lt;/a&gt; counts its tool calls (e.g. looking up a game’s hiscores, or reading a blog post) as GoatCounter events too, so that we can see whether, and how, it’s used. These are sent from our server, not your browser, and never include your IP address or User-Agent header. Each records only which tool was called, what for (e.g. which game, or which post), and the name of the MCP client that called it (e.g. the AI assistant). The same details go in our server logs.&lt;/p&gt;
&lt;h3 id=&quot;Specifics-of-handling-log-files-and-IP-addresses&quot;&gt;Specifics of handling log files and IP addresses&lt;/h3&gt;
&lt;p&gt;When you visit our website, we automatically log your IP address, which is recognized by our web server.&lt;/p&gt;
&lt;p&gt;We use IP addresses:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To help us administer arcade.ly sites - specifically to ensure we provide a good service to users across the globe - and to collect broad demographic information for aggregate use and to track your engagement with us,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To isolate and prevent abuse of our website that might compromise security or degrade the experience we can offer to legitimate users,&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To record the country each score in our hiscore tables was submitted from. Cloudflare determines the country from your IP address and passes us only a two-letter country code: that code is all we store with your score - your IP address is never stored in our hiscore database.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the case of (1) and (2) records may be stored for up to 7 days. In the case of (3), the country code is kept with your score, along with the rest of its record, indefinitely.&lt;/p&gt;
&lt;h2 id=&quot;Non-personal-information&quot;&gt;Non personal information&lt;/h2&gt;
&lt;p&gt;arcade.ly may share non-personal aggregate or summary information regarding its customers with partners or other third parties, such as information relating to the number of users linking to our website from their sites but arcade.ly does not sell or share any information at the individual level.&lt;/p&gt;
&lt;p&gt;We also use non-personal aggregate or summary information to monitor use of the site, which helps us to identify and fix problems as they occur.&lt;/p&gt;
&lt;h2 id=&quot;Cookies&quot;&gt;Cookies&lt;/h2&gt;
&lt;h3 id=&quot;Introduction-to-cookies&quot;&gt;Introduction to cookies&lt;/h3&gt;
&lt;p&gt;Cookies are very small text files saved to your computer and used to track session information and user navigation around a website. However, from a legislative point of view the term “cookies” covers the following&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cookies: very small text files stored on computer, as described above&lt;/li&gt;
&lt;li&gt;Web storage (formerly known as isolated storage): another storage mechanism that allows storage of data on your computer in the form of key/value pairs&lt;/li&gt;
&lt;li&gt;IndexedDB: allows storage of more complex, structured data on your computer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many website still just use cookies, but more complex websites and web applications will often use web storage or IndexedDB to store information about the state of the site or application, or to support offline operation.&lt;/p&gt;
&lt;p&gt;arcade.ly uses a mixture of cookies and web storage for a variety of purposes, described below, but from the point of view or PECR and similar legislation, both mechanisms fall into the category of “cookies”.&lt;/p&gt;
&lt;p&gt;arcade.ly only uses &lt;a href=&quot;https://arcade.ly/blog/2017/01/01/terms-and-conditions/#How-arcade.ly-uses-essential-cookies&quot;&gt;essential cookies&lt;/a&gt;, and doesn’t use cookies for advertising, tracking or analytics.&lt;/p&gt;
&lt;h3 id=&quot;How-arcade.ly-uses-essential-cookies&quot;&gt;How arcade.ly uses essential cookies&lt;/h3&gt;
&lt;p&gt;Essential cookies are those that are absolutely required for the site to work properly. Without them the site would malfunction in ways that would make it very difficult or frustrating to use.&lt;/p&gt;
&lt;p&gt;arcade.ly uses three types of essential cookie:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;A session cookie that is used to keep track of information related to your session: your games, hiscores, and validation/anti-cheating functionality.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A cookie that records that you’ve seen and agreed to our cookie notice. If we didn’t do this we’d have to show it to you every time you visit the site, which would get pretty annoying.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your site and game preferences: whether music and sound effects are enabled, and the visual settings you choose on our &lt;a href=&quot;https://arcade.ly/settings&quot;&gt;settings page&lt;/a&gt;. These are only stored when you change a setting, and stay on your device: they’re never sent to us or anyone else. Strictly, arcade.ly uses web storage for this but, as previously stated, from a legislative perspective web storage falls into the “cookie” category.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because these only remember the choices you make, they count as essential and can’t be turned off. If you’d rather they weren’t kept, you can clear this site’s data in your browser at any time: everything will go back to its default settings.&lt;/p&gt;
&lt;h2 id=&quot;Your-rights&quot;&gt;Your rights&lt;/h2&gt;
&lt;p&gt;Under the GDPR, you have rights as an individual which you can exercise in relation to the information we hold about you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Confirmation of personal data that is being processed&lt;/li&gt;
&lt;li&gt;Access to your personal data&lt;/li&gt;
&lt;li&gt;Other supplementary information as referred to (the information provided in our privacy policy)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you object to the way we are processing your data, or would like us to erase your personal data, contact &amp;#119;&amp;#x65;&amp;#98;&amp;#x6d;&amp;#97;&amp;#x73;&amp;#116;&amp;#x65;&amp;#114;&amp;#x40;&amp;#97;&amp;#x72;&amp;#99;&amp;#x61;&amp;#100;&amp;#x65;&amp;#46;&amp;#x6c;&amp;#121;.&lt;/p&gt;
&lt;h2 id=&quot;Complaints-and-queries&quot;&gt;Complaints and queries&lt;/h2&gt;
&lt;p&gt;arcade.ly tries to meet the highest standards when collecting and using personal information. We take complaints very seriously. If you feel our collection or use of information is unfair, misleading or inappropriate, we encourage you to bring this to our attention. We also welcome any suggestions for improving our procedures.&lt;/p&gt;
&lt;p&gt;If you have any questions regarding this policy, or wish to make a complaint about the way we’ve handled your personal information, contact &amp;#119;&amp;#x65;&amp;#98;&amp;#x6d;&amp;#97;&amp;#x73;&amp;#116;&amp;#x65;&amp;#114;&amp;#x40;&amp;#97;&amp;#x72;&amp;#99;&amp;#x61;&amp;#100;&amp;#x65;&amp;#46;&amp;#x6c;&amp;#121;.&lt;/p&gt;
&lt;p&gt;If you are unhappy with how we have used your data, you can complain to a supervisory authority. In the UK, this is the &lt;a href=&quot;https://ico.org.uk/global/contact-us/&quot;&gt;Information Comissioner’s Office&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;Reporting-security-issues&quot;&gt;Reporting security issues&lt;/h2&gt;
&lt;p&gt;We have implemented technology and policies to help safeguard your privacy from unauthorized access and improper use. For example, arcade.ly itself exclusively communicates over HTTPS, and our servers are firewalled and protected appropriately.&lt;/p&gt;
&lt;p&gt;If you become aware of a security vulnerability in any of arcade.ly’s services or websites, contact &amp;#119;&amp;#x65;&amp;#98;&amp;#x6d;&amp;#97;&amp;#x73;&amp;#116;&amp;#x65;&amp;#114;&amp;#x40;&amp;#97;&amp;#x72;&amp;#99;&amp;#x61;&amp;#100;&amp;#x65;&amp;#46;&amp;#x6c;&amp;#121;.&lt;/p&gt;
&lt;p&gt;We encourage the responsible disclosure of security issues, and will act quickly on any vulnerabilities reported. We will not take legal action against you if you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provide us with the information needed to reproduce and validate the vulnerability&lt;/li&gt;
&lt;li&gt;Avoid violating the privacy of our customers, staff and other users&lt;/li&gt;
&lt;li&gt;Avoid the destruction of data, or degradation of our services&lt;/li&gt;
&lt;li&gt;Do not modify or access data that is not your own&lt;/li&gt;
&lt;li&gt;Give us a reasonable time to address the issue before making any information public&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;Changes-to-this-privacy-policy&quot;&gt;Changes to this privacy policy&lt;/h2&gt;
&lt;p&gt;We regularly review our privacy policy. This policy was last updated on 27th October 2026.&lt;/p&gt;
&lt;h2 id=&quot;Questions-about-data-privacy&quot;&gt;Questions about data privacy&lt;/h2&gt;
&lt;p&gt;arcade.ly is the data controller for the information you provide unless otherwise stated. If you have any queries about the process or how we handle your information, contact &amp;#119;&amp;#x65;&amp;#98;&amp;#x6d;&amp;#97;&amp;#x73;&amp;#116;&amp;#x65;&amp;#114;&amp;#x40;&amp;#97;&amp;#x72;&amp;#99;&amp;#x61;&amp;#100;&amp;#x65;&amp;#46;&amp;#x6c;&amp;#121;.&lt;/p&gt;
</content>
  </entry>
</feed>