<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Yao-Hui Chua</title>
    <description>The latest articles on Forem by Yao-Hui Chua (@yaaooo).</description>
    <link>https://forem.com/yaaooo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F208420%2F45226dbf-f795-4b74-ad03-5a13d0257c6e.jpeg</url>
      <title>Forem: Yao-Hui Chua</title>
      <link>https://forem.com/yaaooo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/yaaooo"/>
    <language>en</language>
    <item>
      <title>The Cognitive Costs of AI Chatbots and a Framework for Better Design</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Thu, 12 Mar 2026 02:44:01 +0000</pubDate>
      <link>https://forem.com/yaaooo/the-cognitive-costs-of-ai-chatbots-and-a-framework-for-better-design-533l</link>
      <guid>https://forem.com/yaaooo/the-cognitive-costs-of-ai-chatbots-and-a-framework-for-better-design-533l</guid>
      <description>&lt;p&gt;As engineers, when we talk about the "cost" of using AI, we tend to focus on billing costs — the cost of running OpenSearch clusters, invoking models through Bedrock, provisioning instances, etc. It's easy to forget that a frustrating user experience is often the most expensive cost to an organization.&lt;/p&gt;

&lt;p&gt;At a time when companies are trying to aggressively incorporate AI experiences into their products, it may be tempting to want to hop on the bandwagon and package every new product requirement as a single-threaded, AI-powered chat experience. This approach, when applied to the wrong customer need, can wind up incurring a tax on the customer's mental bandwidth.&lt;/p&gt;

&lt;p&gt;To be clear, I don't have professional experience as a UX designer or a product manager. That said, I'd like to think that I've been in enough cross-stakeholder discussions to take notice of design anti-patterns when they occur. I can't talk specifics, so I'll just illustrate this trend with a hypothetical example (and with the help of images generated using Nano Banana 2 ✨).&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Use Case
&lt;/h2&gt;

&lt;p&gt;Suppose you are building a B2B platform that enterprise employees use to book business travel. A key product requirement would be to have a "Policy Exception" process. Specifically, when an employee needs to book a flight or hotel that is over the company's budget (perhaps due to a last-minute project or limited availability), we'll need to ensure that certain conditions are met before the employee can get an exception.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ib8udgwkedrctdkqf6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ib8udgwkedrctdkqf6i.png" alt="B2B Travel Booking Dashboard" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On paper, this looks like a perfect use case for an AI-powered chat assistant. You might conceive of an "AI Concierge" which chats with the employee, asks for justifications, checks the company handbook, and approves the booking. However, we begin to see cracks in this model once we try to scale this out to complex tasks that require fine-grained user input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cognitive Overload
&lt;/h2&gt;

&lt;p&gt;Chats are, by their very nature, "single-threaded experiences" — the user submits a query, and the agent responds.&lt;/p&gt;

&lt;p&gt;When a user manages their travel plans, they aren't necessarily just looking at a single data point, or even a single series of data points. Instead, they may very well be making different decisions across different entities (e.g. making adjustments to multiple trips, with each trip being associated with a flight, a hotel, and a car rental).&lt;/p&gt;

&lt;p&gt;An AI agent might ask: "I see your hotel is $100 over budget. Can you provide a justification?" As the employee works through their hotel budget, they might realize that changing their flight date would make a cheaper hotel available, or that the car rental for a separate trip needs to be revised.&lt;/p&gt;

&lt;p&gt;In a chat window, context management and backtracking can be tough for the user. In a dashboard, the UI holds the state; in a chat, the user's brain fills this role. Specifically, customers would have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the information that they've been presented with fresh in their mind&lt;/li&gt;
&lt;li&gt;Scroll back up to find previous details if they've forgotten them&lt;/li&gt;
&lt;li&gt;Interrupt the current thread if backtracking is required&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb97hkz4m4fybd7cigghm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb97hkz4m4fybd7cigghm.png" alt="Travel Booking Dashboard" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For complex tasks involving multiple configurable items, a spatial UI (e.g. a dashboard), may very well be superior to a linear UI (e.g. a chat interface). Dashboards allow the user to navigate as they wish, whereas a chat "locks" them in the current discussion and makes it difficult for them to retain a bird's eye view of the current state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deterministic vs Non-Deterministic Workflows
&lt;/h2&gt;

&lt;p&gt;It is easy for product, design, and engineering leaders to conflate &lt;em&gt;agentification&lt;/em&gt; with &lt;em&gt;automation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Returning to our travel booking use case — in this example, the rules for approving a flight are usually deterministic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;flight &amp;gt; 6 hours&lt;/code&gt;, then &lt;code&gt;Business Class = Allowed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;booking is &amp;lt; 24 hours away&lt;/code&gt;, then &lt;code&gt;Price Cap = +50%&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LLMs are fundamentally probabilistic. Using a non-deterministic agent to handle rules that are strictly deterministic introduces risk, latency, and "hallucination" potential. If the rules are already well-defined, it may be wiser to prioritize building out your business logic behind a deterministic, client-facing API first. You can worry about exposing this as a tool to an AI agent later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Playbook for Product and Design Teams
&lt;/h2&gt;

&lt;p&gt;Before committing to building an agentic chat interface for a core workflow, product managers and designers should ask themselves the following questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Information Complexity:&lt;/strong&gt; Are we dealing with multiple entities that each require a series of user decisions? If yes, does the user need to be able to review the state of all entities, or is this more of a "select-and-forget" type of workflow? &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Real Estate:&lt;/strong&gt; Is there sufficient space to reasonably present the decisions at stake in a single-threaded conversation? In particular, do you have mobile users and have you considered their needs?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic vs Non-Deterministic:&lt;/strong&gt; Are the rules governing your new workflow largely binary (e.g. if X, then Y), or do they require some amount of subjective, human-like judgment?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency of User Input:&lt;/strong&gt; Is it actually faster for a user to address their issue with a sentence, or to click a button?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can see where I'm going with this. A use case that has &lt;strong&gt;high information complexity&lt;/strong&gt;, &lt;strong&gt;limited real estate&lt;/strong&gt;, &lt;strong&gt;strictly deterministic rules&lt;/strong&gt; and a need for &lt;strong&gt;efficient user input&lt;/strong&gt; is likely best served by a static user experience as the &lt;em&gt;primary&lt;/em&gt; interface. An AI chatbot can serve as the &lt;em&gt;secondary&lt;/em&gt; interface which handles the "long-tail" of subjective questions that a static UI can't predict.&lt;/p&gt;

&lt;p&gt;It goes without saying that the two types of experiences are not mutually exclusive. Your product team can absolutely build a static UX alongside an agentic one. However, it is important to prioritize the experience that makes the most sense for your customers. To return to my original example — a "Policy Exception" customer journey should be built with a dashboard first and supplemented with a chatbot later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best of Both Worlds: A Hybrid Approach
&lt;/h2&gt;

&lt;p&gt;With this in mind, it's worth noting that there are ways to blend deterministic workflows with non-deterministic ones. In fact, I would argue that most products should strive to offer this type of hybrid UX, so that customers can select the UX journey that best addresses their needs.&lt;/p&gt;

&lt;p&gt;One option is to have your AI agent as a supplementary assistant that talks users through the main experience. Several products, such as TurboTax, do this well:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz410yw39iqdbjri1zqtm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz410yw39iqdbjri1zqtm.png" alt="Travel Booking Dashboard with a Chat Side Panel" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows your user to have a bird's eye view of what's at stake, take actions as needed, while being able to communicate with an agent to get deeper insight.&lt;/p&gt;

&lt;p&gt;Alternatively, you can also program your AI chat experience in a way that "locks users" into deterministic workflows when needed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e8a0i50lnqs7wxz0ss1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2e8a0i50lnqs7wxz0ss1.png" alt="Travel Booking Dashboard with Deterministic Widgets" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this approach, customers have to complete a bounded, deterministic workflow contained within a UI widget before continuing to speak with the agent. If we go back to the "Policy Exception" use case I described, I wouldn't be a fan of this approach, because it gives the user less agency and prevents them from interjecting with questions.&lt;/p&gt;

&lt;p&gt;That said, there's a definitely a good time and place for this type of UX. For example, surfacing an "Add to Cart" button in the chat experience is an excellent choice — Information Complexity in this situation is somewhat low and the user saves on having to type out their request (e.g. "Please add this item to my cart").&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In a nutshell, don't blindly hop on the AI bandwagon! Customer-facing AI chatbots are a powerful tool and can elevate your core user experience. However, depending on your use case, you may need to build out a reliable static experience first before supplementing it with an agentic one.&lt;/p&gt;

&lt;p&gt;As a front-end engineer, there are many exciting things to look out for in the realm of developing robust "static-agentic" hybrid user experiences. For example, Google has previewed plans to define a specialized UI protocol called &lt;a href="https://github.com/google/A2UI/" rel="noopener noreferrer"&gt;A2UI&lt;/a&gt; that bridges the gap between the traditional text responses from agents and the need for highly interactive UI widgets. In the coming months and years, I also expect we'll see plenty of new patterns emerge around how parent web applications keep their state in sync with their child chatbot widgets.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>uxdesign</category>
      <category>uidesign</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AWS Cognito and IAM for Front-End Developers</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Fri, 16 Sep 2022 03:12:26 +0000</pubDate>
      <link>https://forem.com/yaaooo/aws-cognito-and-iam-for-front-end-developers-29fc</link>
      <guid>https://forem.com/yaaooo/aws-cognito-and-iam-for-front-end-developers-29fc</guid>
      <description>&lt;p&gt;Whenever you build a web application, you'll often need to provision accounts for your users. By &lt;em&gt;users&lt;/em&gt;, I'm specifically referring to &lt;em&gt;customers&lt;/em&gt; who interact with your services via your JavaScript/TypeScript frontend.&lt;/p&gt;

&lt;p&gt;Managing these user accounts comes with plenty of challenges. Not only will you need to persist user profiles, you'll need to figure out how users can be organized into groups, how to store credentials, what kind of auth mechanisms would be suitable, and so on. &lt;/p&gt;

&lt;p&gt;If you're a front-end developer who mainly cares about building enjoyable web experiences, these concerns might seem tedious to deal with. Fortunately, &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html" rel="noopener noreferrer"&gt;AWS Cognito&lt;/a&gt; handles them for you.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://yao.page/posts/identity-and-access-management-aws/" rel="noopener noreferrer"&gt;previous blog post&lt;/a&gt;, I covered some basic concepts around &lt;a href="https://aws.amazon.com/iam/" rel="noopener noreferrer"&gt;Identity and Access Management (IAM)&lt;/a&gt;. Now, I'd like to build on these concepts by explaining how front-end developers can easily use AWS' Cognito and IAM services to set up authentication and authorization for customer-facing applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Cognito
&lt;/h2&gt;

&lt;p&gt;As mentioned, AWS Cognito handles all the nasty user management work. When using Cognito, you really just need to be familiar with two offerings: &lt;strong&gt;User pools&lt;/strong&gt; and &lt;strong&gt;Identity pools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To pull a quote from the Cognito console: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ms4mqzg7uz8zadpc5s9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ms4mqzg7uz8zadpc5s9.png" alt="A summary of user pools and identity pools" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;User pools are user directories that provide sign-up and sign-in options for your app users. Identity pools provide AWS credentials to grant your users access to other AWS services.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  User Pools
&lt;/h3&gt;

&lt;p&gt;The term "user pool" is an intuitive one, as it refers to a collection of users (which can be organized into groups).&lt;/p&gt;

&lt;p&gt;Cognito provides plenty of features for customizing how this collections of users can be managed. Amongst other things, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa.html" rel="noopener noreferrer"&gt;Enable multi-factor authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-configuring-federation-with-social-idp.html" rel="noopener noreferrer"&gt;Set up federation with identity providers&lt;/a&gt; such as Google or Facebook&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html" rel="noopener noreferrer"&gt;Run customized logic&lt;/a&gt; as users progress through their auth-related user journeys (e.g. signing up, signing in).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a user from your user pool logs into your application successfully, they'll have access to an &lt;a href="https://docs.amplify.aws/lib/auth/overview/q/platform/js/#accessing-aws-services" rel="noopener noreferrer"&gt;ID (JWT) token from Cognito&lt;/a&gt;. This token can be used to obtain a set of AWS credentials corresponding to a specific &lt;a href="https://yao.page/posts/identity-and-access-management-aws/#roles" rel="noopener noreferrer"&gt;IAM role&lt;/a&gt;. At this point, you might be wondering: How do we relate Cognito users to IAM identities at all? &lt;/p&gt;

&lt;h3&gt;
  
  
  Identity Pools
&lt;/h3&gt;

&lt;p&gt;This is where identity pools come in.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/tAUmz94O2Qo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Identity pools are a bit trickier to understand. The identities we're dealing with &lt;em&gt;are&lt;/em&gt; IAM identities, which means their settings (e.g. permission policies) are defined in IAM, not Cognito.&lt;/p&gt;

&lt;p&gt;I prefer to think of an identity pool as a &lt;strong&gt;collection of &lt;em&gt;associations&lt;/em&gt; between IAM roles and Cognito user pools&lt;/strong&gt;. For example, given a user pool &lt;code&gt;MyUserPool&lt;/code&gt;, we may want to define an identity pool which assigns one IAM role (e.g. &lt;code&gt;MyAuthenticatedRole&lt;/code&gt;) for users who are authenticated and another IAM role (e.g. &lt;code&gt;MyUnauthenticatedRole&lt;/code&gt;) for users who are not.&lt;/p&gt;

&lt;p&gt;In other words, these identity pools specify the relationships between user pools and IAM roles. As such, Cognito users are able to &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html" rel="noopener noreferrer"&gt;exchange Cognito tokens for AWS credentials&lt;/a&gt;. Front-end clients &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html" rel="noopener noreferrer"&gt;receive these credentials from STS&lt;/a&gt;, which allows Cognito users to interact with AWS services. &lt;/p&gt;

&lt;h2&gt;
  
  
  In Practice
&lt;/h2&gt;

&lt;p&gt;The steps for getting started with Cognito and IAM are relatively straightforward. On your JavaScript/TypeScript frontend, I recommend using the &lt;a href="https://docs.amplify.aws/lib/q/platform/js/" rel="noopener noreferrer"&gt;Amplify SDK&lt;/a&gt;, which supports many common authentication and authorization use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Console TODOs
&lt;/h3&gt;

&lt;p&gt;On the AWS Console, you'll have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Cognito user pool with an app client representing your front-end web application.&lt;/li&gt;
&lt;li&gt;Create a Cognito identity pool with roles for authenticated and unauthenticated users. As you create the identity pool, be sure to link it to your user pool and app client by listing Cognito as an  &lt;strong&gt;Authentication provider&lt;/strong&gt;. Also, ensure your authenticated and unauthenticated roles have the necessary IAM policies in place for them to interact with your AWS services, such as &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt; and &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Front-End Application Code TODOs
&lt;/h3&gt;

&lt;p&gt;In your front-end application code, you'll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store the region, user pool, identity pool id, and app client id in a configuration object (e.g. &lt;code&gt;config&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Initialize Amplify with this config object (e.g. &lt;code&gt;Amplify.configure(config)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use Amplify's &lt;code&gt;Auth&lt;/code&gt; module for your auth-related operations. For example, you can simply invoke &lt;code&gt;Amplify.Auth.signUp(...)&lt;/code&gt; to register a user and add them to your user pool.&lt;/li&gt;
&lt;li&gt;Use Amplify's other modules for interacting with other services. For example, you can use &lt;code&gt;Amplify.API&lt;/code&gt; to speak with your API Gateway endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing worth calling out with Amplify's &lt;code&gt;API&lt;/code&gt; module is that it handles a lot of the IAM-based authorization work for you. &lt;/p&gt;

&lt;p&gt;Under the hood, when you invoke a method such as &lt;code&gt;Amplify.API.post(...)&lt;/code&gt;, the module derives temporary AWS credentials corresponding to the appropriate role (e.g. &lt;code&gt;MyAuthenticatedRole&lt;/code&gt;). These credentials are used to sign the frontend request payload before sending it to API Gateway. See &lt;a href="https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html" rel="noopener noreferrer"&gt;this article on SigV4&lt;/a&gt; for details on the signing process.&lt;/p&gt;

&lt;p&gt;For an overview of what the architecture I've described looks like, check out &lt;a href="https://catalog.us-east-1.prod.workshops.aws/workshops/bc60f0b2-991f-4df9-933c-234a67e75179/en-US/module-2/opt-exts" rel="noopener noreferrer"&gt;this section from an AWS Identity Workshop&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Note that using Cognito with IAM is not strictly necessary. For example, you can set up your endpoints on API Gateway to &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html" rel="noopener noreferrer"&gt;directly rely on Cognito user pools for authorization&lt;/a&gt;. Personally, I prefer the IAM-based approach, because it aligns "how Cognito users interact with APIs" with "how AWS services generally interact with each other".&lt;/p&gt;

&lt;p&gt;I'm constantly impressed by how much AWS is able to abstract out these software building blocks to help developers scaffold their applications quickly. For anyone looking to get their hands dirty with the tools covered, here are some useful references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Beginner's Guide to Cognito from &lt;a href="https://www.youtube.com/c/BeABetterDev" rel="noopener noreferrer"&gt;one of my favorite channels&lt;/a&gt;:   &lt;iframe src="https://www.youtube.com/embed/QEGo6ZoN-ao"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://catalog.us-east-1.prod.workshops.aws/workshops/bc60f0b2-991f-4df9-933c-234a67e75179/en-US" rel="noopener noreferrer"&gt;Workshop: Using Amazon Cognito for Serverless Apps&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/mobile/building-an-application-with-aws-amplify-amazon-cognito-and-an-openid-connect-identity-provider/" rel="noopener noreferrer"&gt;Building an application with AWS Amplify, Amazon Cognito, and an OpenID Connect Identity Provider&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/security-iam.html" rel="noopener noreferrer"&gt;Identity and Access Management for Amplify&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Werewolf: A New Neo-Noir VSCode Theme</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Wed, 02 Jun 2021 18:48:33 +0000</pubDate>
      <link>https://forem.com/yaaooo/werewolf-a-new-vscode-theme-2l5b</link>
      <guid>https://forem.com/yaaooo/werewolf-a-new-vscode-theme-2l5b</guid>
      <description>&lt;p&gt;Telltale's &lt;em&gt;The Wolf Among Us&lt;/em&gt; was released in 2013 and is arguably one of the company's strongest entries in its catalog of story-driven games. The series isn't perfect, but it does have a very cinematic quality to it. The art design and narrative elements blend together really well to create a world which oscillates between fantasy and reality.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/_htfCzTjCpA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The game's thoughtful use of colors, in particular, stood out to me so much that I felt compelled to port some of its neo-noir vibes over to my workspace. A brand new VSCode theme was the perfect way to capture the spirit of this masterpiece.&lt;/p&gt;

&lt;p&gt;You can find the final product &lt;a href="https://marketplace.visualstudio.com/items?itemName=yaaooo.werewolf" rel="noopener noreferrer"&gt;on the VSCode Marketplace&lt;/a&gt; and its corresponding repository &lt;a href="https://github.com/yaaooo/werewolf-vscode-theme" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful Resources
&lt;/h2&gt;

&lt;p&gt;Before I started working on my new theme, I searched for tutorials that could lead me through this process.&lt;/p&gt;

&lt;p&gt;To save you time, the best guide out there at this moment is &lt;a href="https://css-tricks.com/creating-a-vs-code-theme/" rel="noopener noreferrer"&gt;Sarah Drasner's article on CSS Tricks&lt;/a&gt;. It's a handy resource that walks you through the initial setup, configuration basics, and even some important points on accessibility. Alternatively, you can also take a peek at VSCode's official documentation for &lt;a href="https://code.visualstudio.com/api/get-started/your-first-extension" rel="noopener noreferrer"&gt;getting started with your first extension&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Throughout the theming process, I had some takeaways that I'll briefly share with you in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencing Art
&lt;/h2&gt;

&lt;p&gt;If you're basing your color palette on some art you've discovered, you might want to find a couple of expressive images that capture the look and feel you're gunning for. While working on my theme, I used the following piece of concept art pulled from ArtStation by &lt;a href="https://www.artstation.com/artwork/N9v45" rel="noopener noreferrer"&gt;Gray Rogers&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk402kr3vn3ela7q3gtfo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk402kr3vn3ela7q3gtfo.jpg" alt="Fabletown at Night from ArtStation by Gray Rogers" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This depiction of the cityscape was important in helping me determine the kind of mood I wanted to evoke with my choice of colors. I understood that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The theme's backdrop had to be a mix of darker shades (variants of purple and red) &lt;/li&gt;
&lt;li&gt;Points of interest could be highlighted with bursts of neon (variants of blue and green) or yellow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eventually led to combinations of colors like the one you see below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5ukwqg3pucx0i1wh9ub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5ukwqg3pucx0i1wh9ub.png" alt="A glimpse of my theme in Python" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each time I spun up VSCode to iterate on my theme's colors, I adjusted my IDE's window and positioned it right next to my reference image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwnubnc0i52fjltq255o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwnubnc0i52fjltq255o.png" alt="Placing my IDE and my reference image side by side" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This provided me with a sanity check of sorts. I was able to look at the current state of my theme and compare it to the art piece: If my theme felt like an extension of its source of inspiration, then I knew I was on the right track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calibrating Colors
&lt;/h2&gt;

&lt;p&gt;As you might've noticed in some of the code snippets, not every noteworthy piece of syntax can be differentiated with single versions of blue, green, and yellow.&lt;/p&gt;

&lt;p&gt;Thus, I had to expand my selection of colors by exploring different compositions of RGB values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgpurazy9cromwiqqqie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgpurazy9cromwiqqqie.png" alt="A glimpse of my theme in React" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, I used &lt;a href="https://www.color-hex.com/color/43bf90" rel="noopener noreferrer"&gt;#43BF90&lt;/a&gt; for &lt;strong&gt;integers/floats&lt;/strong&gt; and &lt;a href="https://www.color-hex.com/color/7accc9" rel="noopener noreferrer"&gt;#7ACCC9&lt;/a&gt; for &lt;strong&gt;strings&lt;/strong&gt;. Both hex codes have a substantial amount of green in them (which makes them appear somewhat similar) but the latter also has a substantial amount of blue (which gives string primitives a more tealish glow and helps them stand out slightly from their numerical counterparts).&lt;/p&gt;

&lt;p&gt;You can imagine extending this approach to type annotations as well. In my case, I used a darker orange for primitive type references (e.g. &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;) and used a color resembling gold for complex object types (e.g. &lt;code&gt;Props&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To me, it makes sense to use RGB values that are slight variations of each other when coloring tokens that are functionally similar, because it feels like an intuitive way of classifying related chunks of information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming the UI before the Syntax
&lt;/h2&gt;

&lt;p&gt;To be clear, building a VSCode theme mostly just involves assigning hex values to keys in a JSON file that permits common violations such as trailing commas and comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Werewolf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;colors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activityBar.background&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#220033&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.background&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#1D0622&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ... VSCode interface colors&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tokenColors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scope&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;punctuation.definition.comment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foreground&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#6464B3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// ... Syntax highlighting colors &lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two major sections here: &lt;code&gt;colors&lt;/code&gt; and &lt;code&gt;tokenColors&lt;/code&gt;.  &lt;code&gt;colors&lt;/code&gt; manages the different components in your VSCode interface (e.g. your editor, sidebar, terminal, etc), while &lt;code&gt;tokenColors&lt;/code&gt; handles syntax highlighting. &lt;/p&gt;

&lt;p&gt;My suggestion is to work through your preferred &lt;code&gt;colors&lt;/code&gt; first by going through the &lt;a href="https://code.visualstudio.com/api/references/theme-color" rel="noopener noreferrer"&gt;list of available options in the VSCode docs&lt;/a&gt;. You can think of these &lt;code&gt;color&lt;/code&gt; settings as being responsible for composing the "frame" within which your code will be displayed. Updating the background colors of your editor, sidebar, and terminal is enough to radically change the overall appearance of your IDE.&lt;/p&gt;

&lt;p&gt;Configuring &lt;code&gt;tokenColors&lt;/code&gt;, on the other hand, is a trickier task that I wouldn't want to front-load. &lt;a href="https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide" rel="noopener noreferrer"&gt;Syntax highlighting&lt;/a&gt; can be as involved as you'd like it to be; you can easily find yourself tinkering with minor details that demand a lot of consideration (e.g. your preferred colors for brackets, comments, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Back
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos1zzcwb04iquzezl7vz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fos1zzcwb04iquzezl7vz.png" alt="Custom banner by Natalie Christian Tan" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I named this theme &lt;em&gt;Werewolf&lt;/em&gt; (after the main character of the series) and &lt;a href="https://marketplace.visualstudio.com/items?itemName=yaaooo.werewolf" rel="noopener noreferrer"&gt;shared it publicly&lt;/a&gt; a few months ago. Some minor administration was required too; I had to set up proper channels and templates for submitting pull requests and issue reports on the &lt;a href="https://github.com/yaaooo/werewolf-vscode-theme" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking back, I'm not sure this project taught me substantial things about web development, but spending those hours playing around with a bunch of colors like a kid was a pretty fun and relaxing experience.&lt;/p&gt;

&lt;p&gt;My only regret is not shipping this out earlier because &lt;em&gt;The Wolf Among Us&lt;/em&gt; is a pretty dated game at this point. If there's a certain visual language out there that inspires you, I encourage you to take a couple of evenings off to give theming your VSCode a shot!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Warcraft III and Web Development</title>
      <dc:creator>Yao-Hui Chua</dc:creator>
      <pubDate>Tue, 20 Oct 2020 02:06:23 +0000</pubDate>
      <link>https://forem.com/yaaooo/warcraft-iii-and-web-development-5151</link>
      <guid>https://forem.com/yaaooo/warcraft-iii-and-web-development-5151</guid>
      <description>&lt;p&gt;I ended my stint as a web engineer at &lt;a href="https://www.carousell.com/" rel="noopener noreferrer"&gt;Carousell&lt;/a&gt; a few weeks ago. With the extra time I had on my hands, I dusted off a couple of old laptops and wandered through various files and folders from my teenage life.&lt;/p&gt;

&lt;p&gt;One of the gems I came across was a custom Warcraft III map I created in the mid-2000s that I never got around to finishing. I had planted units (i.e. character models) in their respective groups and had shaped up the terrain to look fairly immersive, but the bulk of the gameplay was still incomplete.&lt;/p&gt;

&lt;p&gt;I figured I owed it my younger self to see the project through, so I dug up my old Warcraft III keys, re-installed the game, and started hacking away on the &lt;a href="https://en.wikipedia.org/wiki/Warcraft_III_World_Editor" rel="noopener noreferrer"&gt;World Editor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Back In Time
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0iru7gg5l0wq4zm3frik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0iru7gg5l0wq4zm3frik.png" alt="A view of Warcraft III's World Editor" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was definitely strange revisiting the World Editor after more than a decade. Back in the day, I didn't have the slightest idea about the basics of programming, so I sort of fumbled my way through variables and conditionals. I usually tell people that my first experience with programming was Harvard's CS50, but perhaps Warcraft –– yes, Warcraft –– prepared me in ways I never realised.&lt;/p&gt;

&lt;p&gt;There are a number of similarities between map editing and web development. This isn't exactly a profound insight, as you'd expect to find overlapping patterns between most types of user-facing applications anyway. Still, I think it'll be fun to explore this space and draw some comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triggers / Event Listeners
&lt;/h2&gt;

&lt;p&gt;Warcraft's World Editor is composed of multiple editors such as the Object Editor, the Sound Editor, and the Trigger Editor. The Trigger Editor, in particular, manages a collection of &lt;em&gt;triggers&lt;/em&gt; which are each responsible for coordinating a specific in-game behaviour.&lt;/p&gt;

&lt;p&gt;Suppose you want to kickstart a cinematic sequence when a unit enters a particular area. The corresponding trigger might look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff86i0jdorha408fr3xq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff86i0jdorha408fr3xq7.png" alt="A trigger in the Trigger Editor" width="660" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every trigger is divided into three components: Events, Conditions, and Actions. There are several &lt;a href="https://world-editor-tutorials.thehelper.net/triggers.php" rel="noopener noreferrer"&gt;online tutorials&lt;/a&gt; which do a solid job of explaining the rudiments, but experienced programmers will have an easy time understanding the terms: Whenever an Event occurs, the Warcraft runtime confirms if all specified Conditions have been satisfied before it runs a series of defined Actions.&lt;/p&gt;

&lt;p&gt;As web developers, a huge part of our job involves working with event listeners, which are functionally identical to these triggers. We watch for browser events (such as &lt;code&gt;resize&lt;/code&gt; and &lt;code&gt;keydown&lt;/code&gt;) and respond to them by executing handlers which may subsequently invoke web APIs (such as timers and XMLHTTPRequests).&lt;/p&gt;

&lt;p&gt;In any event-based system, a common mistake people make is forgetting to unsubscribe listeners after they've become irrelevant. Allowing a trigger to repeat itself more than once in a Warcraft map can make for a disjointed gaming experience. Similarly, in the case of web applications, failing to remove listeners can easily lead to memory leaks and unwanted side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layers / Browser Tools
&lt;/h2&gt;

&lt;p&gt;Deciding how to place units, designate regions, angle cameras, and curate the terrain — these are all important aspects of a custom Warcraft map.&lt;/p&gt;

&lt;p&gt;In the World Editor, each of these concerns are owned by a specific &lt;em&gt;layer&lt;/em&gt;. For example, we can only move units around the map with the cursor if the Unit Palette has been selected. Likewise, if you wanted to modify the texture of the ground, you'd have to engage the Terrain Palette. We can't select items across layers and modify them simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbhuhkq5txjaaw112s4yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbhuhkq5txjaaw112s4yg.png" alt="Viewing the map through the lens of the Camera Palette" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the realm of web development, we often find ourselves switching between the templating (HTML), styling (CSS), and interactivity (JavaScript) tools available to us on the browser. The boundaries between them are a lot fuzzier thanks to inlined styles/scripts in HTML and the fluidity of frameworks such as React and Vue which allow us to blend the different syntaxes together (e.g. JSX, MDX).&lt;/p&gt;

&lt;p&gt;Using triggers, map authors can direct elements from one layer to interact with elements from another (e.g. creating a specific type of unit at region X). This is kind of similar to how web developers use JavaScript to make changes to the DOM or CSSOM. Although the separation of concerns takes a fair bit of getting used to in both cases, it goes a long way providing people with a more cohesive development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Player Journeys / User Journeys
&lt;/h2&gt;

&lt;p&gt;At the risk of stating the obvious: As the complexity of a Warcraft map grows, the number of triggers involved naturally increases. Hence, it's important to put some thought into how triggers are classified, so that map authors have an easier time reviewing and modifying them.&lt;/p&gt;

&lt;p&gt;For single-player maps with multiple acts or chapters, it can be difficult to keep track of the triggers used at each point of the player's journey. I've found it appropriate to group triggers by the &lt;em&gt;Quests&lt;/em&gt; they are associated with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpogr622s3dfao2qu75df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpogr622s3dfao2qu75df.png" alt="Grouping triggers by their associated Quests" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each quest can be thought of as an independent slice of the game. One quest might require the player to bring character A to point X, while another might task the player to survive enemy attacks for a specified amount of time. Unless you go out of your way to design a map that heavily relies on choice and consequence, the triggers of one quest usually don't have much bearing on the triggers of another. Thus, it makes sense to bunch up triggers together based on the quests they belong to.&lt;/p&gt;

&lt;p&gt;In web applications, many user journeys can span multiple pages. For instance, making a purchase on an e-commerce platform can take a user from the product details page, to the cart, and then to the checkout screen. In the same vein, I've found it helpful to modularise frontends by grouping data operations by their corresponding pages.&lt;/p&gt;

&lt;p&gt;In that sense, one might say that the relationship between quests and player journeys in Warcraft maps is somewhat analogous to the relationship between pages and user journeys in large web applications. &lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxmbsqf26axb5vruqywac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxmbsqf26axb5vruqywac.png" alt="An in-game screenshot" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ultimately, the challenges of map editing are still very different from web development. This is especially the case if you're creating a custom map with narrative elements. Beyond designing the in-game logic, you'll also have to get your hands dirty with other domains such as cinematography, writing, and pacing.&lt;/p&gt;

&lt;p&gt;There are plenty of details I haven't gotten into in this article. The Warcraft Trigger Editor, at its core, is just a GUI for defining scripted behaviours. Blizzard's JASS language allows map authors to tap into all kinds of in-game possibilities.&lt;/p&gt;

&lt;p&gt;Today, the presence of platforms such as &lt;a href="[http://dreams.mediamolecule.com/](http://dreams.mediamolecule.com/)"&gt;Dreams&lt;/a&gt; lowers the barriers to entry for people to create games without fussing too much over the lower-level details. It's exciting to see where this field is headed and what kinds of fancy creations will emerge. Personally, I'm keeping an eye out for updates from &lt;a href="https://youtu.be/-ir9Yc0ezNo" rel="noopener noreferrer"&gt;this Avatar remake&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/SAEyhW_D1Ms"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Feel free to watch the finished product on YouTube! It's not terribly exciting and it pales in comparison to some of the more impressive works out there, but I'm satisfied with what I got out of the creative process. My goal with this exercise was to simply reconnect with my past self while dipping my toes in as many aspects of map editing as possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing and directing cinematic sequences&lt;/li&gt;
&lt;li&gt;Managing quests and their associated triggers&lt;/li&gt;
&lt;li&gt;Playing with music, environment effects, and sounds&lt;/li&gt;
&lt;li&gt;Importing and using custom models made by the community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've played games like Warcraft in the past, I hope this post inspires you to have fun in a similar fashion!&lt;/p&gt;

</description>
      <category>sideprojects</category>
      <category>watercooler</category>
    </item>
  </channel>
</rss>
