<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rob's Blog]]></title><description><![CDATA[Rob's Blog]]></description><link>https://blog.robruizr.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 21:24:42 GMT</lastBuildDate><atom:link href="https://blog.robruizr.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Normality vs Novelty: Embracing Context as UX Designers]]></title><description><![CDATA[What is a User Experience Designer's job? To design a User Interface? To improve how a product looks? Creating the packaging of a physical product? No, there's more to it than that. 
A User Experience Designer's job can be summarized in five words: 
...]]></description><link>https://blog.robruizr.dev/normality-vs-novelty-embracing-context-as-ux-designers</link><guid isPermaLink="true">https://blog.robruizr.dev/normality-vs-novelty-embracing-context-as-ux-designers</guid><category><![CDATA[UX]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Roberto Ruiz]]></dc:creator><pubDate>Mon, 18 Apr 2022 21:34:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672432239026/5751fc78-4f11-41b1-8d66-077196d5a4f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What is a User Experience Designer's job? To design a User Interface? To improve how a product looks? Creating the packaging of a physical product? No, there's more to it than that. </p>
<p>A User Experience Designer's job can be summarized in five words: 
We design solutions to problems. </p>
<p>Be it a problem that is already known, or one that is yet to be discovered, we try to find optimal ways to solve issues. We talk to users, look for their needs, design ways to fill these needs, test our solution, and repeat. This cycle known as the User-Centred Design is the core of our discipline. Once we're done with this cycle (or until management says it's enough) we end up with a product that helps the user fix whatever problem they had, while having a good experience. We can categorize these products in three groups:</p>
<ul>
<li>Products that end up worsening the users' lives</li>
<li>Products that bring their users to normality</li>
<li>Products that improve their users' lives</li>
</ul>
<p>I won't talk much about the first group, as they shouldn't make it out of the User-Centred Design cycle (except when seen as objects of art or study, like <a target="_blank" href="https://www.itsnicethat.com/articles/impossible-objects">Jacques Carelman's Impossible Objects</a>). However, I'd like to discuss more about the second and third groups.</p>
<h2 id="heading-products-that-restore-normality">Products that restore normality</h2>
<p>Let's start by stating something that might be obvious, then go from there: We humans are social creatures. We tend to live by what we call Social Norms. We are influenced by what other people do, or by things we are expected to do. These norms affect the way we behave, dress, and live. And when we deviate from them, we may start looking for ways to restore normality. Products that fall into this category make us go from our non-standard, unnormalized way to one that falls into social norms. </p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ussxpj0fvgw5s57sdu8k.jpg" alt="An example of this is the Football tape: a plastic tape that can be made into a ball. While the product itself is not a bad idea, and it bring the users to normality (having a ball), it does not consider their context nor does it make this better than having a real ball." /></p>
<blockquote>
<p>An example of this is the Football tape: a plastic tape that can be made into a ball. While the product itself is not a bad idea, and it bring the users to normality (having a ball), it does not consider their context nor does it make this better than having a real ball.</p>
</blockquote>
<p>Examples of these products can be seen throughout the evolution of the COVID pandemic during the last two years. From the start, we looked for ways to go back to normality:</p>
<ul>
<li>Online meetings that replaced in-person ones</li>
<li>Virtual classrooms or conference rooms where attendants had to take their own virtual seats</li>
<li>Corona online concerts</li>
</ul>
<p>Even when trying to replace their physical counterparts, these products just wouldn't fill our needs. We were so used to an in-person context that we even brought its restrictions to the virtual world. Yes, these products kind of give us what we lost while not being inherently bad. They do help us solve a problem we have at hand. But, what if we could do more than this?</p>
<h2 id="heading-products-that-improve-their-users-lives">Products that improve their users' lives</h2>
<p>You could discuss that products from the previous category also improve their users lives. While that might be true, and we don't want to leave any product behind, I want to focus more on the type of product that does more just restore normality. Not only do these bring their users to balance with social norms, but they also help us go well beyond them (Plus Ultra!). These are the products that become objects of study, and that give us a "Wow!" factor.</p>
<p>Going back to our pandemic examples, we can see a few cases where novel solutions were implemented, taking us to a better position than if we tried to restore normality:</p>
<ul>
<li>Game worlds where we could create new experiences with our loved ones (E.g. Nintendo's Animal Crossing)</li>
<li>Flying around in a virtual world to attend a conference room, without seat restrictions</li>
<li>Attending Ariana Grande's Fortnite concerts while dressed as your favourite character</li>
</ul>
<p>These products don't try to imitate their real-world counterpart. They embrace the context they exist in, and create a solution based on the restrictions and flexibilities it has. This allows us to create new experiences that probably wouldn't have existed had we stayed in normality.</p>
<p>So where would that leave your product, if it stayed away from normality and embraced its context?</p>
]]></content:encoded></item><item><title><![CDATA[Lightning Environment Installation and Setup]]></title><description><![CDATA[Let's start this series by answering a big question about this framework:
Why not use HTML + CSS + JS?
See, using these common web technologies is not as efficient as we might expect. In fact, using these technologies can result in our applications o...]]></description><link>https://blog.robruizr.dev/lightning-environment-installation-and-setup</link><guid isPermaLink="true">https://blog.robruizr.dev/lightning-environment-installation-and-setup</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[javascript framework]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[WebGL]]></category><dc:creator><![CDATA[Roberto Ruiz]]></dc:creator><pubDate>Fri, 22 Oct 2021 14:12:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634275937753/WDH-qX0nS.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's start this series by answering a big question about this framework:</p>
<h2 id="why-not-use-html-css-js">Why not use HTML + CSS + JS?</h2>
<p>See, using these common web technologies is not as efficient as we might expect. In fact, <a target="_blank" href="https://fxdigital.uk/challenges-html5-connected-tv-applications/">using these technologies can result in our applications overloading CTV devices</a>. A great way to avoid this is by using a tool that employs our devices to their full extent, such as WebGL. This tool helps us overcome performance-related difficulties. It also allows us to:</p>
<ul>
<li>Create easy-to-deploy applications because of its cross-platform nature.</li>
<li><a target="_blank" href="https://www.khronos.org/webgl/wiki/WebGL_and_OpenGL">Do automatic memory management</a> and hardware-accelerated 2d and 3d graphics.</li>
<li>Avoid issues with vendor-specific technology implementation. Looking at you, Safari!</li>
</ul>
<p>But, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL">WebGL is not the easiest tool to use or understand</a>. Luckily for us, some tools allow us to use most of WebGL's features while using only JavaScript.</p>
<p>Lightning is one of these tools. It's a highly optimized framework that utilizes both CPU and GPU to use all our CTV devices to their fullest. It uses a lot of Object-Oriented Programming concepts at its core. It also has a lot of out-of-the-box tools that help us solve compatibility issues with any CTV platform. </p>
<p>We now understand why we're interested in using Lightning. Let'start by installing everything we'll need to create our first app.</p>
<h3 id="installing-lightning-cli-and-creating-our-first-project">Installing Lightning CLI and creating our first project</h3>
<p>The <a target="_blank" href="https://lightningjs.io/docs/#/getting-started/index">official Lightning docs</a> are divided into roughly 4 different sections:</p>
<ul>
<li>Lightning CLI: a Command Line Interface tool used to create and run new Lightning apps.</li>
<li>Lightning SDK: a set of tools and plugins we can use to develop Lightning apps</li>
<li>Lightning UI: a set of components commonly found in TV apps.</li>
<li>Lightning Core: API reference for all the features included in Lightning.</li>
</ul>
<p>To create our new project, we'll need to install the CLI by using either npm or Yarn. We can do so using the following commands:</p>
<pre><code class="lang-bash">npm install -g @lightningjs/cli

<span class="hljs-comment"># or using yarn</span>
yarn global add @lightningjs/cli
</code></pre>
<p>We can then verify that it has been properly installed by using <code>lng -version</code>, which should display a message similar to this:</p>
<pre><code class="lang-bash">Lightning-CLI 2.5.1
</code></pre>
<p>Let's now create our first project. We can do so by using <code>lng create</code>. The CLI will then guide us through a series of questions about our new project. This was my configuration:</p>
<pre><code class="lang-bash">? What is the name of your Lightning App? My First App
? What is the App identifier? (reverse-DNS format) my.first.app
? In what (relative) folder <span class="hljs-keyword">do</span> you want to create the new App? (leave empty to create <span class="hljs-keyword">in</span> current working dir) myFirstLightningApp
? Do you want to <span class="hljs-built_in">enable</span> ESlint? No
? Do you want to install the NPM dependencies now? Yes
? Do you want to initialize an empty GIT repository? No
</code></pre>
<p>This command will install all the dependencies our project needs, so it may take some time to finish.</p>
<p>Once this is finished we can open our directory by using <code>cd myFirstLightningApp</code>. Make sure to change <code>myFirstLightningApp</code> to whatever you called your folder. You can now run your app by using <code>lng dev</code>. If everything was done right, you should see the following message:</p>
<pre><code class="lang-bash">Starting up http-server, serving ./build
Available on:
  http://127.0.0.1:8080
  http://192.168.1.10:8080
</code></pre>
<p>Congratulations! You have now created your first Lightning app. Try opening a web browser and navigating to <code>localhost:8080</code> to check it out. Your browser should display something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634276964298/eXwkC0NzcP.png" alt="Pasted image 20211014230407.png" /></p>
<p>And that's it! Our first Lightning app is ready to be tested using any web browser. Still, it's lacking some things. There are no interactions, no flashy effects, and not much is happening. To do that, we'll start by learning Lightning's most important concepts. We'll do this by configuring and developing more elements in our first app. See you in the next lesson!</p>
]]></content:encoded></item><item><title><![CDATA[Untangling your Logic using State Machines]]></title><description><![CDATA[You may find this article useful if:

You can read JS / Object oriented languages (Python, C++, C#, Java, etc)
You are familiar with writing functions (https://stackoverflow.com/a/4709224)

Introduction
A few weeks ago, I was working on an applicatio...]]></description><link>https://blog.robruizr.dev/untangling-your-logic-using-state-machines</link><guid isPermaLink="true">https://blog.robruizr.dev/untangling-your-logic-using-state-machines</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[clean code]]></category><category><![CDATA[vanilla-js]]></category><dc:creator><![CDATA[Roberto Ruiz]]></dc:creator><pubDate>Fri, 08 Oct 2021 03:51:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1633665061975/C4cPUWy29.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You may find this article useful if:</p>
<ul>
<li>You can read JS / Object oriented languages (Python, C++, C#, Java, etc)</li>
<li>You are familiar with writing functions (https://stackoverflow.com/a/4709224)</li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>A few weeks ago, I was working on an application where I had to control a button's visibility. From the moment I started, I knew the following things:</p>
<ul>
<li>My button could be either visible or invisible.</li>
<li>If any key was pressed while my button was invisible, it would become visible.</li>
<li>When my button became visible, a 3-second timer would start.</li>
<li>If the timer expired, the button would then become invisible.</li>
<li>if a key was pressed while my button was visible, the timer would restart.</li>
</ul>
<p>We could explain this logic using the following diagram:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633664971134/gEBi7PjSZ.png" alt="Diagram of the states that were previously described. There's two circles, one for &quot;visible&quot;, the other one for &quot;invisible&quot;. Both have arrows pointing to each other, representing the transitions that can happen between states." /></p>
<p>I decided it was good enough, so I started coding right away. My code looked something like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Code simplified for explanation purposes</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onKeyPress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(button.visible) {
    restartTimer();
  } <span class="hljs-keyword">else</span> {
    button.visible = <span class="hljs-literal">true</span>;
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">restartTimer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(timer.exists) {
    timer.delete();
  }

  timer = <span class="hljs-keyword">new</span> Timer(<span class="hljs-string">"3 seconds"</span>);
  <span class="hljs-keyword">if</span>(timer.elapsed) {
      button.visible = <span class="hljs-literal">false</span>;
  }
}
</code></pre>
<p>I wasn't really feeling the result. My beautiful button was popping in and out of the screen without much of a transition or animation. I knew the designers in my team wouldn't be happy with my work, so I decided to add some fanciness to my work. I opted for a 1s opacity transition, then got back into coding. I ended with something like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Code simplified for explanation purposes</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onKeyPress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(button.visible) {
    restartTimer();
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Wait for transition to complete.</span>
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"opacity=1"</span>)
    button.visible = <span class="hljs-literal">true</span>;
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">restartTimer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(timer.exists) {
    timer.delete();
  }

  timer = <span class="hljs-keyword">new</span> Timer(<span class="hljs-string">"3 seconds"</span>);
  <span class="hljs-keyword">if</span>(timer.elapsed) {
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"opacity=0"</span>)
      button.visible = <span class="hljs-literal">false</span>;
  }
}
</code></pre>
<p>Yet, this introduced a new bug to my code. Can you spot it? Try going back to the code and see if you can find it.</p>
<p>Did you spot it? Don't worry if not! It took me some time to find it. Here's a clue: What would happen if you pressed a key while a transition is happening? Since you pressed a key, the timer should restart and button's opacity should go back to 1.</p>
<p>Where should I add this? I decided to add a new <code>isFadingOut</code> property to my button, so my code now looked like this:</p>
<pre><code class="lang-js"><span class="hljs-comment">// Code simplified for explanation purposes</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onKeyPress</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(button.isFadingOut) {
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"opacity=1"</span>);
    button.visible = <span class="hljs-literal">true</span>;
  }
  <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(button.visible) {
    restartTimer();
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Wait for transition to complete.</span>
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"opacity=1"</span>)
    button.visible = <span class="hljs-literal">true</span>;
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">restartTimer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(timer.exists) {
    timer.delete();
  }

  timer = <span class="hljs-keyword">new</span> Timer(<span class="hljs-string">"3 seconds"</span>);
  <span class="hljs-keyword">if</span>(timer.elapsed) {
    <span class="hljs-comment">// Wait for transition to complete.</span>
    button.isFadingOut = <span class="hljs-literal">true</span>;
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"opacity=0"</span>)
    button.isFadingOut = <span class="hljs-literal">false</span>;
      button.visible = <span class="hljs-literal">false</span>;
  }
}
</code></pre>
<p>This ended up creating a new list of bugs, most of them caused by a <a target="_blank" href="https://stackoverflow.com/a/34550">race condition</a>. This was getting out of hand! Now I had to deal with several timers at the same time. What if I had to add a new <code>fadingIn</code> state? How much would that mess up my code? I decided it was time to change the way I approached the problem.</p>
<h2 id="state-machines-save-the-day">State Machines save the day.</h2>
<p>You may have noticed that the opacity transition created a new element in our diagram:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633665023675/FFO0v4kVV.png" alt="A new circle has been added to the previous diagram. This new circle represent the &quot;fadingIn&quot; state that can happen when going from visible to invisible." /></p>
<p>This diagram represents a State Machine. It is one of the ways how one can be drawn. State machine are a great tool to visualize all the <strong>states</strong> and <strong>transitions</strong> in our application. Every circle represents a state, while every arrow is a transition between states. They also help us see all the different inputs needed for a transition between states to happen. All in all, they are a great way to untangle almost any sort of boolean mess</p>
<h2 id="this-is-all-great-but-how-do-i-use-them">This is all great but, how do I use them?</h2>
<p>One of the ways in which we can implement a State Machine is using <code>enumerators</code>. They don't exist in JavaScript natively, but we can simulate one using an object:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> buttonStates = {
  <span class="hljs-comment">// You could also map these to a number instead of the same string,</span>
  <span class="hljs-comment">// but this is personal preference as it's easier to debug.</span>
  <span class="hljs-attr">fadingOut</span>: <span class="hljs-string">"fadingOut"</span>,
  <span class="hljs-attr">visible</span>: <span class="hljs-string">"visible"</span>,
  <span class="hljs-attr">invisible</span>: <span class="hljs-string">"invisible"</span>
};
</code></pre>
<p>We can then store the current state of our button in a property: </p>
<pre><code class="lang-js"><span class="hljs-comment">// start with a default state</span>
button.state = buttonStates.visible;
</code></pre>
<p>We'll need to add a new function in charge of transitioning between states:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">changeState</span>(<span class="hljs-params">newState</span>) </span>{
  button.state = newState;

  <span class="hljs-keyword">if</span>(newState === buttonStates.visible) {
    clearTransitions();
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"alpha=1"</span>);
    restartTimer();
  }

  <span class="hljs-keyword">if</span>(newState === buttonStates.fadingOut) {
    waitTransition(<span class="hljs-string">"1 second"</span>, <span class="hljs-string">"alpha=0"</span>)
  }
}
</code></pre>
<p>Finally, we need to adapt both our previous functions to take our new state into account:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onKeyPress</span>(<span class="hljs-params"></span>)</span>{
  <span class="hljs-keyword">if</span>(button.state === buttonStates.visible) {
    restartTimer();
  }

  <span class="hljs-keyword">if</span>(button.state === buttonStates.invisible) {
    changeState(buttonStates.visible) 
  }

  <span class="hljs-keyword">if</span>(button.state === buttonStates.fadingOut) {
      changeState(buttonStates.visible)
  } 
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">restartTimer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span>(timer.exists) {
    timer.delete();
  }

  timer = <span class="hljs-keyword">new</span> Timer(<span class="hljs-string">"3 seconds"</span>);
  <span class="hljs-keyword">if</span>(timer.elapsed) {
    changeState(buttonStates.fadingOut)
  }
}
</code></pre>
<p>This is not only easier to debug, but it also makes it simpler to add new States to our button. As an example, you could add a new <code>fadingIn</code> state by:</p>
<ol>
<li>Adding it to our enumerator</li>
<li>Adding a new if statement both in <code>changeState</code> and <code>restartTimer</code>.</li>
</ol>
<p>Once this is done, you may notice this logic won't easily clash with what we previously did. Every state has a different behaviour that is split into its own block.</p>
<h2 id="when-do-i-use-them">When do I use them?</h2>
<p>As I mentioned, State machines are a great tool for several use cases. They are implemented in day-to-day tools and can be seen in modern libraries such as <a target="_blank" href="https://xstate.js.org/">xstate</a>. However, they shouldn't <em>always</em> be used. There are some cases where a State Machine may even make our logic more complicated. Here's a list of pros and cons I've found while working with them:</p>
<p>Pros:</p>
<ul>
<li>They make apps easier to debug, by separating each state into its own block</li>
<li>It's easy to add new states into your application</li>
<li>They make your code easier to read.</li>
</ul>
<p>Cons:</p>
<ul>
<li>They have a learning curve, people who are not familiar with them may find them confusing.</li>
<li>Not the best way to implement that on-off button you're working on.</li>
</ul>
<h2 id="learn-more-about-state-machines">Learn more about State Machines</h2>
<p>Using enums and if/else statements is not the only way to create a state machine. This is only one of the approaches you can take to do it. Here's a list of places where you can learn more about them:</p>
<ul>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Finite-state_machine">Finite-state machines on Wikipedia</a></li>
<li><a target="_blank" href="https://xstate.js.org/docs/">XState's docs</a></li>
<li><a target="_blank" href="http://gameprogrammingpatterns.com/state.html">State machines in game development</a></li>
<li><a target="_blank" href="https://gamedev.stackexchange.com/a/7555">This great explanation I found on Stackoverflow while writing this article</a></li>
</ul>
<p>Hey! Thank you for reading my article. If you learned something new or enjoyed my daily dev struggles, please follow me on Twitter: <a target="_blank" href="https://twitter.com/robruizrdevs">@robruizrdevs</a>.</p>
<p>See you soon! :)</p>
]]></content:encoded></item></channel></rss>