<?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[Danidre]]></title><description><![CDATA[I'm Danidre, an author, game programmer, web developer, and software engineer!]]></description><link>https://blog.danidre.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 13 Apr 2026 23:42:02 GMT</lastBuildDate><atom:link href="https://blog.danidre.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Created My Own Frontend Framework]]></title><description><![CDATA[Introduction
In this final week's submission for Hashnode's #4articles4weeks Writeathon, I'm circling back to DCL, a custom javascript frontend develop-as-needed framework that I created while working on Projectree.
What Is DCL
DCL is short for Danid...]]></description><link>https://blog.danidre.com/how-i-created-my-own-frontend-framework</link><guid isPermaLink="true">https://blog.danidre.com/how-i-created-my-own-frontend-framework</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week4]]></category><category><![CDATA[javascript framework]]></category><category><![CDATA[custom]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Sun, 11 Sep 2022 21:18:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1662924265396/-AGD41C7u.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In this final week's submission for Hashnode's <a target="_blank" href="https://townhall.hashnode.com/4-articles-in-4-weeks-hashnode-writeathon?source=danidre_blog">#4articles4weeks Writeathon</a>, I'm circling back to DCL, a custom javascript frontend develop-as-needed framework that I created while working on <a target="_blank" href="https://projectree.net">Projectree</a>.</p>
<h1 id="heading-what-is-dcl">What Is DCL</h1>
<p>DCL is short for Danidre's Client Library. I needed to spend July 2022 working on an open source project that uses PlanetScale, however, half of that time was spent working on the framework to use in the project.</p>
<h2 id="heading-dcl-features">DCL Features:</h2>
<p>Since DCL was developed alongside Projectree, it is not a fully featured developer rich library. I started with rendering components and handling state with JavaScript, and expanded as I needed more features.</p>
<p>At the July 2022's release of Projectree, DCL supports:</p>
<ul>
<li>Nested components</li>
<li>Routing</li>
<li>Component props</li>
<li>Component state</li>
<li>Global context</li>
<li>Component rerendering</li>
<li><a class="post-section-overview" href="#heading-breaking-input">Input management*</a></li>
</ul>
<h1 id="heading-why-i-created-dcl">Why I Created DCL</h1>
<p>The last time I created a project with a frontend library (React; February 2020), I spent 2 months learning and applying what I learnt. Also, many build steps were involved, and tedious setup was required. For the hackathon, I only had 1 month. I wanted to work on an application with only HTML, CSS, and JavaScript. I also wanted full control of the application, instead of spending days trying to solve an error if I got stuck.</p>
<p>At the same time, I wanted to be able to reuse elements on a page instead of repeating code on multiple pages, and I wanted to be able to have values update without having to read and write from elements in JavaScript.</p>
<blockquote>
<p>Instead of <code>let age = document.getElementById("age").value</code> and <code>document.getElementById("age").value = newAge</code> for getting and setting values, I just wanted to change a variable once, and its represented element should change automatically.</p>
</blockquote>
<h2 id="heading-benefits-of-dcl">Benefits of DCL</h2>
<p>As a result, using DCL has many perks:</p>
<ul>
<li>Easy setup</li>
<li>Minimal JavaScript file</li>
<li>No build time required</li>
<li>Develop, drag and drop to deploy</li>
<li>No complex language necessary</li>
<li>No relearning architecture</li>
</ul>
<h1 id="heading-how-i-created-dcl">How I Created DCL</h1>
<h2 id="heading-components">Components</h2>
<p>The main class in DCL is the <code>DCL</code> class. All other elements can extend this component class. This class handles nested components, unique component state, and more. Below is a simple overview of its functions:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props = {}) {
        <span class="hljs-built_in">this</span>.props = props;
        <span class="hljs-built_in">this</span>.state = {};
        <span class="hljs-built_in">this</span>._dclId = _generateUID();
        <span class="hljs-built_in">this</span>._refs = {
            <span class="hljs-attr">children</span>: [],
            <span class="hljs-attr">stateFuncIDs</span>: [],
            <span class="hljs-attr">createFuncIDs</span>: [],
            <span class="hljs-attr">contextFuncIDs</span>: []
        };
    }

    setState(key, value) {}
    <span class="hljs-keyword">static</span> stateStore = {}

    createFunc(func, ...args) {}
    <span class="hljs-keyword">static</span> funcStore = {}

    onMount() {}
    onUnmount() {}

    _mount(parent) {}
    _unmount() {}

    render() {}
    _getHTML() {}
    _rerender() {}
    <span class="hljs-keyword">static</span> _validateDCLX(dclx = <span class="hljs-string">""</span>) {}

    monitorContext(key, callback, ...args) {}
    <span class="hljs-keyword">static</span> getContext(key) {}
    <span class="hljs-keyword">static</span> setContext(key, value) {}
    <span class="hljs-keyword">static</span> clearContext(key)
}

<span class="hljs-built_in">window</span>.DCL = DCL;
</code></pre>
<h2 id="heading-creating-a-basic-component">Creating a Basic Component</h2>
<p>A simple component can be created using the following: </p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Button</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props);
    }

    render() {
        <span class="hljs-keyword">const</span> onClick = <span class="hljs-built_in">this</span>.props.onClick || <span class="hljs-string">"null"</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;button onclick="<span class="hljs-subst">${onClick}</span>"&gt;<span class="hljs-subst">${<span class="hljs-built_in">this</span>.props.name}</span>&lt;/button&gt;`</span>;
    }
}
</code></pre>
<blockquote>
<p>As possibly assumed, the <code>render</code> function describes how the component should look.</p>
</blockquote>
<p>This <code>Button</code> component renders a button element, with a name passed as a property. When the component is first created, the <code>onMount</code> function is called. This is where developers can make requests to retrieve information from APIs or servers. However, since this <code>Button</code> component does not have a need for it, nothing will happen here.</p>
<h2 id="heading-creating-an-interactive-component">Creating an Interactive Component</h2>
<p>The following component will give a better example of the methods available for each component:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Todo</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props);
        <span class="hljs-built_in">this</span>.state = {
            <span class="hljs-attr">taskname</span>: <span class="hljs-string">""</span>,
        }
    }

    render() {
        <span class="hljs-keyword">const</span> updateTaskname = <span class="hljs-built_in">this</span>.setState(<span class="hljs-string">"taskname"</span>, <span class="hljs-function">(<span class="hljs-params">oldTaskname, evt</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> evt.target.value;
        });
        <span class="hljs-keyword">const</span> displayTask = <span class="hljs-built_in">this</span>.createFunc(<span class="hljs-function">(<span class="hljs-params">evt</span>) =&gt;</span> {
            alert(<span class="hljs-built_in">this</span>.state.taskname);
        });
        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;h1&gt;Task&lt;/h1&gt;
            &lt;input onblur="<span class="hljs-subst">${updateTaskname}</span>" value="<span class="hljs-subst">${<span class="hljs-built_in">this</span>.state.taskname}</span>"/&gt;
            <span class="hljs-subst">${<span class="hljs-keyword">new</span> Button({name:<span class="hljs-string">"Alert Task"</span>, onClick: displayTask}</span>)._mount(this)}
        `</span>
    }
}
</code></pre>
<p>Many things are going on here:</p>
<ul>
<li>The <code>Todo</code> component represents a task that the user can edit. When created, the task (stored as <code>taskname</code>) is empty.</li>
<li>Then, we try to render 3 elements to represent this component: an <code>h1</code> element representing the task label, an <code>input</code> element representing the task itself, and a <code>Button</code> <strong>component</strong> that can display the task's name to the user.</li>
</ul>
<p>If we try to run this, however, we get the following error:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662930484537/o9eio9e6g.JPG" alt="only_1_elem_error.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-validating-components">Validating Components</h2>
<p>Following React's convention, each DCL component should only return one top level element. Instead, the elements should be wrapped into a <code>div</code> or other enclosing element:</p>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> <span class="hljs-string">`
    &lt;div&gt;
        &lt;h1&gt;Task&lt;/h1&gt;
        &lt;input onblur="<span class="hljs-subst">${updateTaskname}</span>" value="<span class="hljs-subst">${<span class="hljs-built_in">this</span>.state.taskname}</span>"/&gt;
        <span class="hljs-subst">${<span class="hljs-keyword">new</span> Button({name:<span class="hljs-string">"Alert Task"</span>, onClick: displayTask}</span>)._mount(this)}
    &lt;/div&gt;
`</span>
</code></pre>
<p>That is because DCL does not have a <a target="_blank" href="https://reactjs.org/docs/faq-internals.html">Virtual DOM</a> that tracks changes in element states. Instead, the <code>_getHTML</code> method creates a temporary element that:</p>
<ol>
<li>Wraps around each component's returned render html:</li>
</ol>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">dcl</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Task<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onblur</span>=<span class="hljs-string">"updateTaskName()"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"displayTask()"</span>&gt;</span>Alert Task<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dcl</span>&gt;</span>
</code></pre>
<ol>
<li>Assigns a unique identifier to itself:</li>
</ol>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">dcl</span> <span class="hljs-attr">data-dcl-id</span>=<span class="hljs-string">"todo_l7xr2462wjgidez7kf"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Task<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onblur</span>=<span class="hljs-string">"updateTaskName()"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"displayTask()"</span>&gt;</span>Alert Task<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dcl</span>&gt;</span>
</code></pre>
<ol>
<li>Passes it to the first child element:</li>
</ol>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">dcl</span> <span class="hljs-attr">data-dcl-id</span>=<span class="hljs-string">"todo_l7xr2462wjgidez7kf"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">data-dcl-id</span>=<span class="hljs-string">"todo_l7xr2462wjgidez7kf"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Task<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onblur</span>=<span class="hljs-string">"updateTaskName()"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"displayTask()"</span>&gt;</span>Alert Task<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">dcl</span>&gt;</span>
</code></pre>
<ol>
<li>And removes itself:</li>
</ol>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">data-dcl-id</span>=<span class="hljs-string">"todo_l7xr2462wjgidez7kf"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Task<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onblur</span>=<span class="hljs-string">"updateTaskName()"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>/&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"displayTask()"</span>&gt;</span>Alert Task<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>That way, the intended html structure of the page is maintained, and DCL is able to recognize which elements need to change upon rerender.</p>
<p><code>DCL</code>'s <code>_validateDCLX</code> method is called internally to ensure that only one top level element is returned for each component.</p>
<blockquote>
<p>In this case, DCLX stands for DCL XML, DCL's markup syntax.</p>
</blockquote>
<p>Now, the task component renders correctly, and is functional:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662930499525/jx_uMuDPw.gif" alt="working_task_component.gif" class="image--center mx-auto" /></p>
<p>As possible imagined, <code>this.setState</code> and <code>this.createFunc</code> methods allow functions to be called on each element. Similar to React, <code>setState</code> changes the component's state and rerenders it to reflect the change.</p>
<h3 id="heading-the-good">The Good</h3>
<p>When <code>setState</code> is triggered, the component searches the DOM for the element with that unique id, removes it, and rerenders the component and its children.</p>
<h3 id="heading-the-bad">The Bad</h3>
<p>Since DCL uses no Virtual DOM, each component must keep track of their nested components (children). That is why the <code>Button</code> has the <code>_mount(this)</code> function. <code>_mount</code> accepts a parent component, and adds that child to the parent's list of components. That way, when a parent component has to be removed or rerendered, it can loop through its children and unmount them appropriately.</p>
<blockquote>
<p>Although it works, the DCLX syntax for nested components is a bit...unappealing.</p>
</blockquote>
<h3 id="heading-the-ugly">The Ugly</h3>
<p>React and other frameworks have build steps that generate the appropriate JavaScript that allows functions and methods in each component to retain their proper scope. However, DCL does not have this, as each component returns the html as a string, which is then appended to the DOM using <code>innerHTML</code>. As a result, functions defined this way cannot be returned as a string and executed correctly.</p>
<p>To address that, the <code>setState</code> and <code>createFunc</code> methods actually store the passed functions in a global scope controlled by DCL (<code>stateStore</code> and <code>funcStore</code>), and returns the string name of the method, so it is included in the <code>innerHTML</code> and "works":</p>
<ol>
<li>The developer defines the function:</li>
</ol>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> updateTaskname = <span class="hljs-built_in">this</span>.setState(<span class="hljs-string">"taskname"</span>, <span class="hljs-function">(<span class="hljs-params">oldTaskname, evt</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> evt.target.value;
});
</code></pre>
<ol>
<li>It is internall added to DCL's <code>stateStore</code>:</li>
</ol>
<pre><code class="lang-js">setState(key, value) {
    <span class="hljs-keyword">const</span> funcID = <span class="hljs-string">`stateID_<span class="hljs-subst">${<span class="hljs-built_in">this</span>._dclId}</span>_<span class="hljs-subst">${_generateUID()}</span>`</span>;

    DCL.stateStore[funcID] = <span class="hljs-function">(<span class="hljs-params">evt</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> value === <span class="hljs-string">"function"</span>) {
            <span class="hljs-keyword">const</span> prevState = _deepClone(<span class="hljs-built_in">this</span>.state);
            <span class="hljs-built_in">this</span>.state[key] = value(prevState, evt);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">this</span>.state[key] = value;
        }

        <span class="hljs-built_in">this</span>._rerender();
    }

    <span class="hljs-comment">// ...other code</span>
}
</code></pre>
<ol>
<li>It is also added to the component's references list (so they can delete it when unmounting):</li>
</ol>
<pre><code class="lang-js">setState(key, value) {
    <span class="hljs-comment">// ...other code</span>

    <span class="hljs-built_in">this</span>._refs.stateFuncIDs.push(funcID);
}
</code></pre>
<ol>
<li>The string representation is finally returned:</li>
</ol>
<pre><code class="lang-js">setState(key, value) {
    <span class="hljs-comment">// ...other code</span>

    <span class="hljs-keyword">return</span> <span class="hljs-string">`DCL.stateStore.<span class="hljs-subst">${funcID}</span>(event)`</span>;
}
</code></pre>
<p>Thus, the innerHTML becomes the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">onblur</span>=<span class="hljs-string">"DCL.stateStore.stateID_todo_l7xr2462wjgidez7kf_l7xr5olmp8tsb1cguf(event)"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>/&gt;</span>
</code></pre>
<blockquote>
<p>Whenever the input element is blurred, it will call the function since it can be found on the webpage.</p>
</blockquote>
<p>The same process occurs with the <code>createFunc</code> method: it is added to DCL's <code>funcStore</code>, referenced by the component so it can be removed later, and returned as a string for the innerHTML.</p>
<p>This method is ugly because it pollutes the webpage's global scope. I do prevent memory leaks by deleting each method when its referenced component is unmounted, however.</p>
<h2 id="heading-monitoring-global-changes">Monitoring Global Changes</h2>
<p>Internally, DCL uses a <a target="_blank" href="https://cloud.google.com/pubsub/docs/overview">pub/sub</a> method to broadcast changes. Each component can 'hook' onto changes using <code>monitorContext</code>. Then, whenever that property is changed, the 'hooked' component rerenders:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Elem1</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props);
        <span class="hljs-built_in">this</span>.num = DCL.getContext(<span class="hljs-string">"number"</span>);
    }
    onMount() {
        <span class="hljs-built_in">this</span>.monitorContext(<span class="hljs-string">"number"</span>, <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> {
            <span class="hljs-built_in">this</span>.num = value;
        });
    }
    render() {
        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;div&gt;
                &lt;h1&gt;Elem 1&lt;/h1&gt;
                &lt;p&gt;Num: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.num}</span>&lt;/p&gt;
            &lt;/div&gt;
        `</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Elem2</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    render() {
        <span class="hljs-keyword">const</span> incrementNum = <span class="hljs-built_in">this</span>.createFunc(<span class="hljs-function">(<span class="hljs-params">evt</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> prevNumber = DCL.getContext(<span class="hljs-string">"number"</span>) || <span class="hljs-number">0</span>;
            DCL.setContext(<span class="hljs-string">"number"</span>, prevNumber + <span class="hljs-number">1</span>);
        });

        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;div&gt;
                &lt;h1&gt;Elem 2&lt;/h1&gt;
                <span class="hljs-subst">${<span class="hljs-keyword">new</span> Button({name:<span class="hljs-string">"Increase Number"</span>, onClick: incrementNum}</span>)._mount(this)}
            &lt;/div&gt;
        `</span>
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props);

        DCL.setContext(<span class="hljs-string">"number"</span>, <span class="hljs-number">0</span>);
    }
    render() {
        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;div&gt;
                <span class="hljs-subst">${<span class="hljs-keyword">new</span> Elem1()._mount(<span class="hljs-built_in">this</span>)}</span>
                <span class="hljs-subst">${<span class="hljs-keyword">new</span> Elem2()._mount(<span class="hljs-built_in">this</span>)}</span>
            &lt;/div&gt;
        `</span>
    }
}
</code></pre>
<p>Whenever the button from <code>Elem2</code> is clicked, DCL's <code>number</code> property increases. Since <code>Elem1</code> is 'hooked' to the <code>number</code> property (from <code>monitorContext</code>), a rerender is triggered each time the number property changes, so <code>Elem1</code> successfully reflects the change each time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662930511361/VhLM_H1zd.gif" alt="elem2_elem1_monitorContext.gif" class="image--center mx-auto" /></p>
<h2 id="heading-handling-page-routes">Handling Page Routes</h2>
<p>One of the intentions of my library was to treat pages as components and render a different component based on the url navigated to. For that, I needed DCL to handle routes as well. Thus, I created a <code>Router</code> component that solves this:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Router</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props);

        <span class="hljs-built_in">this</span>.routes = props.routes || [];
        <span class="hljs-built_in">this</span>.defaultRoute = props.defaultRoute || routes[<span class="hljs-number">0</span>];
        <span class="hljs-built_in">this</span>.view = <span class="hljs-literal">null</span>;
    }
    onMount() {
        <span class="hljs-comment">// redetermine route when the back button is triggered</span>
        <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"popstate"</span>, <span class="hljs-built_in">this</span>.determineRoute);

        <span class="hljs-comment">// navigate to new link without refreshing page if specific element is clicked</span>
        <span class="hljs-built_in">document</span>.body.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">(<span class="hljs-params">evt</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span>(evt.target.matches(<span class="hljs-string">"[data-dcl-link]"</span>)) {
                evt.preventDefault();
                history.pushState(<span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, evt.target.href);
                <span class="hljs-built_in">this</span>.determineRoute();
            }
        });

        <span class="hljs-built_in">this</span>.determineRoute();
    }

    determineRoute() {
        <span class="hljs-keyword">const</span> routes = <span class="hljs-built_in">this</span>.routes;

        <span class="hljs-comment">// test each route for potential match</span>
        <span class="hljs-keyword">const</span> potentialMatches = routes.map(<span class="hljs-function"><span class="hljs-params">route</span> =&gt;</span> ({
            <span class="hljs-attr">route</span>: route,
            <span class="hljs-attr">result</span>: location.pathname.match(<span class="hljs-built_in">this</span>._pathToRegex(route.path))
        }));

        <span class="hljs-keyword">let</span> match = potentialMatches.find(<span class="hljs-function"><span class="hljs-params">potentialMatch</span> =&gt;</span> potentialMatch.result !== <span class="hljs-literal">null</span>);

        <span class="hljs-keyword">if</span> (!match) {
            match = {
                <span class="hljs-attr">route</span>: <span class="hljs-built_in">this</span>.defaultRoute,
                <span class="hljs-attr">result</span>: [location.pathname]
            };
        }


        <span class="hljs-keyword">const</span> props = match.route.props;
        <span class="hljs-keyword">const</span> params = <span class="hljs-built_in">this</span>._getParams(match);
        <span class="hljs-keyword">const</span> query = <span class="hljs-built_in">this</span>._getQuery(location.search);

        DCL.params = params;
        DCL.query = query;

        <span class="hljs-comment">// set view for determined route</span>
        <span class="hljs-built_in">this</span>.view = <span class="hljs-keyword">new</span> match.route.view({...props, params, query});

        <span class="hljs-built_in">this</span>._rerender();
    }

    render() {
        <span class="hljs-keyword">if</span>(!<span class="hljs-built_in">this</span>.view) <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;

        <span class="hljs-keyword">return</span> <span class="hljs-string">`&lt;div&gt;<span class="hljs-subst">${<span class="hljs-built_in">this</span>.view.mount(<span class="hljs-built_in">this</span>)}</span>&lt;/div&gt;`</span>;
    }

    _pathToRegex = <span class="hljs-function"><span class="hljs-params">path</span> =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">"^"</span> + path.replace(<span class="hljs-regexp">/\/?$/g</span>, <span class="hljs-string">""</span>).replace(<span class="hljs-regexp">/\//g</span>, <span class="hljs-string">"\\/"</span>).replace(<span class="hljs-regexp">/:\w+/g</span>, <span class="hljs-string">"([^/]+)"</span>) + <span class="hljs-string">"\/?$"</span>);

    _getParams = <span class="hljs-function"><span class="hljs-params">match</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> values = match.result.slice(<span class="hljs-number">1</span>);
        <span class="hljs-keyword">const</span> keys = <span class="hljs-built_in">Array</span>.from(match.route.path.matchAll(<span class="hljs-regexp">/:(\w+)/g</span>)).map(<span class="hljs-function"><span class="hljs-params">result</span> =&gt;</span> result[<span class="hljs-number">1</span>]);

        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.fromEntries(keys.map(<span class="hljs-function">(<span class="hljs-params">key, i</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> [key, <span class="hljs-built_in">decodeURI</span>(values[i])];
        }));
    };

    _getQuery = <span class="hljs-function"><span class="hljs-params">searchParams</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> urlSearchParams = <span class="hljs-keyword">new</span> URLSearchParams(searchParams);
        <span class="hljs-keyword">const</span> query = <span class="hljs-built_in">Object</span>.fromEntries(urlSearchParams.entries());
        <span class="hljs-keyword">return</span> query;
    };
}
</code></pre>
<p>The <code>Router</code> is a custom standard component that hooks onto the forward and backward navigation of the page and swaps out the component based on the path. The router can be used with the following code:</p>
<pre><code class="lang-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TodoPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    render() {
        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;div&gt;
                &lt;p&gt;ID: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.props.params.id}</span>&lt;/p&gt;
            &lt;/div&gt;
        `</span>
    }
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DCL</span> </span>{
    render() {
        <span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> Router({
            <span class="hljs-attr">routes</span>: [
                { <span class="hljs-attr">path</span>: <span class="hljs-string">"/"</span>, <span class="hljs-attr">view</span>: HomePage, <span class="hljs-attr">props</span>: { <span class="hljs-attr">num</span>: <span class="hljs-number">5</span> } },
                { <span class="hljs-attr">path</span>: <span class="hljs-string">"/todos"</span>, <span class="hljs-attr">view</span>: TodosPage },
                { <span class="hljs-attr">path</span>: <span class="hljs-string">"/todo/:id"</span>, <span class="hljs-attr">view</span>: TodoPage },
            ],
            <span class="hljs-attr">defaultRoute</span>: { <span class="hljs-attr">path</span>: <span class="hljs-string">"/404"</span>, <span class="hljs-attr">view</span>: Page404 },
        });

        <span class="hljs-keyword">return</span> <span class="hljs-string">`
            &lt;div&gt;
                &lt;nav&gt;
                    &lt;a href="/" data-dcl-link&gt;Home&lt;/a&gt;
                    &lt;a href="/todos" data-dcl-link&gt;Todos&lt;/a&gt;
                    &lt;a href="/todo/4" data-dcl-link&gt;4th Todo&lt;/a&gt;
                &lt;/nav&gt;
                <span class="hljs-subst">${<span class="hljs-keyword">new</span> Header().mount(<span class="hljs-built_in">this</span>)}</span>
                    &lt;main&gt;
                        <span class="hljs-subst">${router.mount(<span class="hljs-built_in">this</span>)}</span>
                    &lt;/main&gt;
                <span class="hljs-subst">${<span class="hljs-keyword">new</span> Footer().mount(<span class="hljs-built_in">this</span>)}</span>
            &lt;/div&gt;
        `</span>
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662930525228/i8b1WAFfA.gif" alt="Todo_with_params.gif" class="image--center mx-auto" /></p>
<h1 id="heading-disadvantages-of-dcl">Disadvantages of DCL</h1>
<p>Although DCL solved all of my needs, it was not without bugs that I was not able to solve:</p>
<h2 id="heading-code-smell">Code Smell:</h2>
<p>Different sections of the core class is polluted with spaghetti code, as I gradually built upon DCL alongside Projectree. Some code chunks may be completely useless, but I have not removed it due to continual development.</p>
<h2 id="heading-breaking-input">Breaking Input:</h2>
<p>One major unsolvable issue is how inputs are handled. If an <code>onchange</code> event was used with the <code>&lt;input&gt;</code> element that causes it to rerender every time a value was entered, after each character was pressed, the component would rerender the input and focus would be lost.</p>
<p>Currently, DCL tries to solve this using a hack: It tries to keep track of each selected element per click, and holds an <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/XPath">XPath</a> reference to that element. If the element is a <code>textarea</code> or <code>input</code> element, it stores the selection range.</p>
<p>Additionally, an <code>observeDOM</code> function is defined that waits for a change in the document using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver">MutationObserver</a>. When any component is rerendered, the DOM is considered changed, and DCL attempts to reselect the input element using the previously stored <code>XPath</code>.</p>
<p>Sometimes this works successfully, but this assumes the input element remains in the same location in the webpage. Oftentimes, another element in the document is added or removed arbitrarily, and the <code>XPath</code> causes some other element to be reselected (or an error occurs if no matching <code>XPath</code> is found), which breaks the user experience, as they type into one field and suddenly are typing in another input field.</p>
<p>Other times, if there are too many input elements on the page, the <code>observeDOM</code> tries to reselect an input field before the document rerenders every component, which also breaks user experience, since the input field suddenly loses focus.</p>
<p>I tried to create timeouts to wait a few milliseconds after rerendering before attempting to reselect elements, but this process also breaks for many elements. In the end, DCL currently has no way to determine when all elements have completely rerendered to attempt to reselect, which causes this issue.</p>
<blockquote>
<p>DCL also accounts for asynchronous methods in its API, which makes detecting complete rerenders more difficult.</p>
</blockquote>
<h2 id="heading-wonky-paradigms">Wonky Paradigms:</h2>
<p>DCL supports additional unmentioned options such as <code>middleware</code> functions that are called before each reroute, as well as <code>ignoreRoute</code> checks that can skip a route call if a condition fails (e.g. the user is not authorized). However, with the current flow of the application, sometimes the <code>ignoreRoute</code> is called after the incomplete <code>view</code> has rendered, causing a flicker on the end user's screen. I would have to rearrange some of DCL's routing logic to better control that flow, but it gets diffucult to control since each nested component is called using the <code>_mount()</code> method.</p>
<h2 id="heading-seo-limitations">SEO Limitations:</h2>
<p>As DCL is a single page application (SPA) framework, it is client-side rendered (CSR). A user launches the page and are greeted with the standard HTML template that is then populated into an application when the JavaScript loads. For users, this is assumed as the loading process and is experienced for a few milliseconds to a few seconds, depending on connection speed or other factors. However, web crawlers that index pages usually do not wait around those extra seconds for the JavaScript to be loaded.</p>
<p>Currently, as DCL is a minimal develop-as-needed framework with no build steps, I had not considered supporting server-side rendering (SSR) which would be ideal for search engine optimizations.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>DCL is a develop-as-needed library that I no longer have plans of working on, however, it was very helpful throughout the development of Projectree. From this experience, I have reasons both for and against creating your own frontend framework.</p>
<h2 id="heading-why-you-should-create-your-own-framework">Why You Should Create Your Own Framework</h2>
<p>From working on DCL, I learnt more about the internals of an SPA, and became aware of more browser APIs that I did not know before (such as history API), that can be used to make the developer experience easier. It is thanks to this project that I am able to better appreciate existing frameworks; although each seem complicated, they try in their own ways to make the developer's experience better, and provide as many tools as possible to create web apps.</p>
<p>I believe a developer should strive to make their own framework to understand what happens underneath, as it can also help them understand existing frameworks more.</p>
<h2 id="heading-why-you-should-not-create-your-own-framework">Why You Should Not Create Your Own Framework</h2>
<p>The code and examples in this article are a simplified view of what DCL is, however, it still cannot compare to solutions existing frameworks solve, and features they already provide. Additionally, many groups of senior developers and architects work on existing frameworks, and cater for many more edge cases than DCL does.</p>
<p>While developers should create their own frameworks for the learning experience, unless they are senior developers with industry level experience, it would be better to learn and use an existing framework.</p>
<blockquote>
<p>Unless they are also like me and are too stubborn to learn a new framework; though eventually, I too will have to learn new frameworks.</p>
</blockquote>
<hr />

<p>Eventually, I'd like to recreate Projectree's frontend using <a target="_blank" href="https://reactjs.org/">React</a> or <a target="_blank" href="https://www.solidjs.com/">SolidJS</a> instead. Hopefully it is an easier process since I have first-hand experience. Hopefully, I've also convinced you to try creating your own frontend library too. You know...<em>for the fun of it</em>.</p>
<blockquote>
<p>And also for the learning experience 😉</p>
</blockquote>
<p>Thanks for staying tuned each week for articles this writeathon! You can click the links below to support me or check the unofficial source code yourself:</p>
<ul>
<li>DCL: <a target="_blank" href="https://github.com/danidre14/projectree-frontend/tree/main/static/js/DCL">Source code</a></li>
<li>Projectree: https://projectree.net</li>
<li>Twitter: <a target="_blank" href="https://twitter.com/danidre">@danidre</a></li>
<li>Patreon: <a target="_blank" href="https://www.patreon.com/danidre">Danidre</a></li>
</ul>
<p>Happy learning!</p>
]]></content:encoded></item><item><title><![CDATA[How I Created My Own Authentication System]]></title><description><![CDATA[Overview
If you want to skip straight to the tutorial section of this article, click here: How to create a custom implementation system in Fastify
Introduction
Last July, I participated in the PlanetScale x Hashnode Hackathon, and created Projectree ...]]></description><link>https://blog.danidre.com/how-i-created-my-own-authentication-system</link><guid isPermaLink="true">https://blog.danidre.com/how-i-created-my-own-authentication-system</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week3]]></category><category><![CDATA[authentication]]></category><category><![CDATA[custom]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Mon, 05 Sep 2022 01:33:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1662339527479/3eBIEC_va.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-overview">Overview</h1>
<p>If you want to skip straight to the tutorial section of this article, click here: <a class="post-section-overview" href="#heading-implementation-code">How to create a custom implementation system in Fastify</a></p>
<h1 id="heading-introduction">Introduction</h1>
<p>Last July, I participated in the <a target="_blank" href="https://townhall.hashnode.com/planetscale-hackathon">PlanetScale x Hashnode Hackathon</a>, and created <a target="_blank" href="https://projectree.net">Projectree</a> using a custom JavaScript SPA library on the frontend, Django on the backend, and PlanetScale as the database. I had worked on the frontend, and teamed up with <a target="_blank" href="https://hashnode.com/@Sophyia">Sophia</a> who worked on the backend.</p>
<blockquote>
<p>Projectree is a service that you can use to create simplified showcases of projects you have worked on.</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=QA-KGBiEzpI">https://www.youtube.com/watch?v=QA-KGBiEzpI</a></div>
<p>Since then, I had plans to continue working on Projectree, but unfortunately, Sophia was no longer able to continue working on it with me. Thus, I had to work on the backend myself.</p>
<h1 id="heading-modifying-projectrees-backend">Modifying Projectree's Backend</h1>
<p>Sophia used Django and Django ORM for Projectree's backend. Honestly, I was able to scan the source code on <a target="_blank" href="https://github.com/Sophyia7/projectree-backend">GitHub</a> and understand how she made everything work, but I had no idea how to begin restructuring everything to develop it for my specific <a target="_blank" href="https://blog.danidre.com/introducing-projectree#heading-development-roadmap">plans and goals</a> for Projectree.</p>
<p>I decided to remake Projectree's backend myself, so I could have full control of the stack, and understanding of how to further customize it. I told Sophia about it, who was quite supportive of the idea. I changed the existing projectree frontend domain to <a target="_blank" href="https://legacy.projectree.net">https://legacy.projectree.net</a> and she enabled the Django backend's CORS for that domain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662339356290/kxDEUYCsL.jpeg" alt="SupportiveSophia.jpeg" class="image--center mx-auto" /></p>
<blockquote>
<p>The Django backend is actually deployed to Heroku with their free dynos, but by November 28th, <a target="_blank" href="https://blog.heroku.com/next-chapter">this form of hosting will no longer be possible</a>. By then, the Django backend will most likely be sunsetted as well.</p>
</blockquote>
<p>Then I started recreating the backend.</p>
<h1 id="heading-choosing-a-stack">Choosing a Stack</h1>
<p>Projectree's plans exceeded the existing capabilities possible at the end of the hackathon, so to achieve them, I needed something I was familiar and comfortable with. My go to tech stack is NodeJS, Express, MongoDB, but I wanted to try something new as well.</p>
<p>For the new backend, I decided to use (and learn) NodeJS, Fastify, Prisma, and SQLite.</p>
<h2 id="heading-why-i-chose-each-option">Why I chose each option</h2>
<h3 id="heading-fastify">Fastify</h3>
<p>Projectree's backend acts more like an API, that accepts requests and sends responses. <a target="_blank" href="https://www.fastify.io/">Fastify</a> is better suited for APIs since it is fast, and not as bloated as Express.</p>
<h3 id="heading-prisma">Prisma</h3>
<p>One thing I passionately detested about SQL databases, is the migration process.</p>
<blockquote>
<p>It was for this reason that I teamed up with a backend developer; they can battle with databases, and I will focus on the frontend.</p>
</blockquote>
<p>However, most of the data in Projectree is relational, so a relational database would be a more suitable choice.</p>
<p><a target="_blank" href="https://www.prisma.io/">Prisma</a> allows you to use many databases such as PostgreSQL and MongoDB, and it would manage database migrations, and changing schemas for you. Additionally, it provides very intuitive APIs for querying data, creating records, and much more.</p>
<h3 id="heading-sqlite">SQLite</h3>
<p>Although Prisma removes the tedious migration work for me, I still need to set up databases both locally and in production (whether I used MySQL, MariaDB, PostgreSQL, etc). I was trying to avoid that setup, and decided to use <a target="_blank" href="https://www.sqlite.org/index.html">SQLite</a>. It was boasted as being super fast, with no tedious setup, and can reside right in your application directory.</p>
<blockquote>
<p>Many developers on discord advocated for its usage: <br /> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662338980671/vcMIChp_0.jpeg" alt="SQLiteEnthusiasts.jpeg" class="image--center mx-auto" /></p>
</blockquote>
<h1 id="heading-creating-an-auth-system">Creating an Auth System</h1>
<p>Projectree's first backend used Django, and came with its own <a target="_blank" href="https://docs.djangoproject.com/en/4.1/topics/auth/">authentication system</a>, which made developing easy, as it allowed for easy integration with the database using Django ORM. I needed to remake this in Fastify.</p>
<p>From previous projects, I only had experience with Express' <a target="_blank" href="http://www.passportjs.org/">Passport</a> middleware for authentication, using its <a target="_blank" href="http://www.passportjs.org/packages/passport-local/">local</a> strategy.</p>
<blockquote>
<p>The local strategy allows you to authenticate using a username and password, rather than an OAuth method that uses Discord, Facebook, Twitter, Gmail, etc to sign in to the account.</p>
</blockquote>
<p>At first, I tried looking for a <a target="_blank" href="https://github.com/fastify/fastify-passport">passport middleware counterpart for Fastify</a> or using a <a target="_blank" href="https://github.com/fastify/fastify-auth">Fastify specific authentication library</a>, but in the attitude of learning new things, I decided to learn how to implement authentication of my own.</p>
<h2 id="heading-what-is-auth">What is auth?</h2>
<p>Auth is used both when talking about authentication, and authorization. Authentication deals with determing the identity of the person making the request, and authorization deals with whether the authenticated person can perform a requested action or not.</p>
<p>Auth can be implemented in multiple ways such as sessions or JWTs, and each method has their pros and cons. For Projectree, I decided to use sessions.</p>
<h2 id="heading-the-general-idea">The general idea</h2>
<p>The server is generally able to determine who is making a request through some form of identifier. When the user logs in (is authenticated), they are assigned an identifier, which they use in subsequent requests. The server will verify that the identifier is valid while processing each request, and perform different actions based on whether the user is allowed (is authorized) to make that request or not.</p>
<h2 id="heading-my-custom-auth-theory">My custom auth theory</h2>
<p>Middleware like Passport allows for a session store (like a database) to store the session details of the users. However, as I was implementing my own auth, I did not want to additionally handle storing sessions in a database, setting schemas, etc. As a result, I opted for storing the session identifier in the client's cookie when they sign in (for authentication); and then checking that cookie per request for authorization. Then, when the client logs out, that cookie will be destroyed.</p>
<blockquote>
<p><strong>Trivia:</strong> I honestly do not fully understand how the server keeps track of the sessions for anonymous users, even. Maybe the existing libraries simply set and read cookie variables, so a repeating request identifies them. Maybe it involves the TCP handshake of the client and server, and they are able to recognize the client's subsequent request because...of magic?</p>
</blockquote>
<h1 id="heading-addressing-security-concerns">Addressing Security Concerns</h1>
<p>While this method seems simple, there are many flaws to it:</p>
<ul>
<li>Clients can modify their cookies, which means a malicious user can modify their session details to be someone else.</li>
<li>Clients can read their cookies. Third party scripts would then be able to execute scripts that send cookie data to their own malicious data centers.</li>
<li>Data can be accessible through <code>http</code> (unsecure) connections.</li>
<li>Cross site request forgeries (CSRF) can occur where a bad site perform an action through your good site, which sends your cookies, and the server is unable to determine whether it is a legit authenticated request, or a forged authorized one.</li>
</ul>
<p>To address these, I made the following configurations with the cookies:</p>
<ul>
<li>Enable <code>httpOnly</code> cookies. That way, only the server can access and modify the cookies.</li>
<li>Enable <code>secure</code> cookies. That way, cookies will only be sent on <code>https</code> (encrypted) connections.</li>
<li>Use a cookie <code>secret</code>. Existing libraries allow a cookie secret that can be used to sign the cookie. That way, the server will be able to determine whether the cookie was manually modified by another source than itself.<br /><br />Some libraries also garble the cookie data when signed, so it is not human readable; however, the general practice is to not store sensitive information in the cookie in any case.</li>
<li>Set the <code>sameSite</code> attribute to <code>"lax"</code> or <code>"strict"</code>. These help the browser decide whether to send cookies along with cross-site requests, which can mitigate CSRFs.</li>
</ul>
<blockquote>
<p>To distinguish between the Strict and Lax sameSite values, check <a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#samesite-cookie-attribute">this OWASP resource</a>.</p>
</blockquote>
<h1 id="heading-implementation-code">Implementation Code</h1>
<p>The remainder of this article could be read more as a tutorial instead.</p>
<h2 id="heading-how-to-create-a-custom-implementation-system-in-fastify">How to create a custom implementation system in Fastify</h2>
<blockquote>
<p>Unlike other tutorials that build a mini application along with the auth code, this one will simply focus on the auth code, with assumptions and pseudocode in other places.</p>
</blockquote>
<h3 id="heading-0-prerequisites">0) Prerequisites</h3>
<p>This tutorial uses NodeJS, so ensure you have nodejs installed.</p>
<h3 id="heading-1-install-the-dependencies">1) Install the dependencies</h3>
<p>For this tutorial, the main packages/modules you will need are fastify, fastify's secure session, and fastify plugin.</p>
<pre><code class="lang-sh">npm i fastify @fastify/secure-session fastify-plugin
</code></pre>
<ul>
<li><code>fastify</code>: The web framework we are creating the API with.</li>
<li><code>@fastify/secure-session</code>: Creates a secure stateless cookie session for Fastify.<ul>
<li>I chose this over <code>@fastify/session</code> because this module stores the session data in the client's cookie, instead of requiring a database like <code>@fastify/session</code>.</li>
<li>I also chose this over <code>@fastify/cookie</code> because the syntax of accessing and modifying the cookies with this library was easier than <code>@fastify/cookie</code>.</li>
</ul>
</li>
<li><code>fastify-plugin</code>: A module that you use to allow your plugin to be accessible throughout the intended Fastify contexts.</li>
</ul>
<h3 id="heading-2-create-your-server-file">2) Create your server file</h3>
<p>Next, you create your main server file (<code>server.js</code>) and add the following code to it:</p>
<pre><code class="lang-js"><span class="hljs-comment">// server.js</span>

<span class="hljs-keyword">const</span> fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fastify"</span>);
<span class="hljs-keyword">const</span> fastifySecureSession = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@fastify/secure-session"</span>);

<span class="hljs-keyword">const</span> secureSessionConfig = {
    <span class="hljs-attr">secret</span>: process.env.COOKIE_SECRET || <span class="hljs-string">"averylogphrasebiggerthanthirtytwochars"</span>,
    <span class="hljs-attr">cookie</span>: {
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/"</span>
    }
}
<span class="hljs-keyword">if</span> (process.env.NODE_ENV === <span class="hljs-string">"production"</span>) {
    secureSessionConfig.cookie.secure = <span class="hljs-literal">true</span> <span class="hljs-comment">// serve secure cookies</span>
    secureSessionConfig.cookie.httpOnly = <span class="hljs-literal">true</span> <span class="hljs-comment">// serve cookies only accessible by the server</span>
    secureSessionConfig.cookie.sameSite = <span class="hljs-string">"strict"</span>; <span class="hljs-comment">// serve cookies only from site</span>
}

<span class="hljs-keyword">const</span> app = fastify({<span class="hljs-attr">ignoreTrailingSlash</span>: <span class="hljs-literal">true</span>});

app.register(fastifySecureSession, secureSessionConfig);

app.listen({ <span class="hljs-attr">port</span>: process.env.PORT || <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">err, address</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) {
        app.log.error(err);
        process.exit(<span class="hljs-number">1</span>);
    }
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server started on address "</span> + address);
});
</code></pre>
<p>Here we have a basic Fastify server, with a few modifications in preparation for the custom authentication;</p>
<p>First we import <code>fastify</code> and <code>@fastify/secure-session</code>, and initialize them accordingly. Notice that we create a <code>secureSessionConfig</code> object with the options to use with fastify's secure session.</p>
<blockquote>
<p>I added the <code>ignoreTrailingSlash</code> property so urls like <code>example.com/home</code> and <code>example.com/home/</code> both point to the same request handler.</p>
</blockquote>
<p>As explained before, we provide a <code>secret</code> to be used for signing the cookie, and in a production environment, we want the cookies to be <code>secure</code>, <code>httpOnly</code> cookies with <code>sameSite</code> <code>"lax"</code> or <code>"strict"</code>. Additionally, we want the default <code>path</code> of the cookies to be at the <code>root</code> of the application.</p>
<blockquote>
<p><strong>Trivia:</strong> I originally left out this <code>path</code> property, but experienced unexpected behaviours in my application: Multiple session cookies were being set on different paths, making multiple users authenticated on the same device, depending on which endpoint was being fetched.</p>
</blockquote>
<p>Then we start the server with <code>app.listen</code>.</p>
<h3 id="heading-3-create-your-custom-auth-module">3) Create your custom auth module</h3>
<p>After that, we want to implement our custom authentication system. We will extend the functionality of Fastify's flow by managing the sessions set in <code>@fastify/secure-session</code> in this file. For that, we use <code>fastify-plugin</code>. Let's create a <code>customAuth.js</code> file in the same directory as the <code>server.js</code> file and add the following code:</p>
<pre><code class="lang-js"><span class="hljs-comment">// customAuth.js</span>

<span class="hljs-keyword">const</span> fp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify-plugin'</span>)
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">customAuth</span>(<span class="hljs-params">fastify, options</span>) </span>{

    fastify.decorateRequest(<span class="hljs-string">"user"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"isAuthenticated"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"logIn"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"logOut"</span>, <span class="hljs-literal">null</span>);

    fastify.addHook(<span class="hljs-string">"onRequest"</span>, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> getUser = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">const</span> user = req.session.get(<span class="hljs-string">"user"</span>);
                <span class="hljs-keyword">if</span> (user) {
                    <span class="hljs-keyword">return</span> user;
                }
                <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
            } <span class="hljs-keyword">catch</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
            }
        }
        req.isAuthenticated = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">return</span> !!getUser();
        }
        req.logOut = <span class="hljs-function">() =&gt;</span> {
            req.session.set(<span class="hljs-string">"user"</span>, <span class="hljs-literal">undefined</span>);
        }
        req.logIn = <span class="hljs-function">(<span class="hljs-params">data, cb</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> done = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">error, user</span>) </span>{
                <span class="hljs-keyword">if</span> (error) {
                    <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                        cb(error, user);
                    <span class="hljs-keyword">return</span>;
                }
                req.session.set(<span class="hljs-string">"user"</span>, user);
                <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                    cb(<span class="hljs-literal">null</span>, user);
            }
            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">if</span> (customAuth.authenticate &amp;&amp; <span class="hljs-keyword">typeof</span> customAuth.authenticate === <span class="hljs-string">"function"</span>) {
                    customAuth.authenticate(data, done);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                        cb(<span class="hljs-string">"Cannot authenticate. No authentication method found."</span>, <span class="hljs-literal">null</span>);
                }
            } <span class="hljs-keyword">catch</span> (err) {
                <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                    cb(err, <span class="hljs-literal">null</span>);
            }
        }
        req.user = getUser();
        next();
    });
}

customAuth.useStrategy = <span class="hljs-function">(<span class="hljs-params">strategy</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (strategy &amp;&amp; <span class="hljs-keyword">typeof</span> strategy === <span class="hljs-string">"function"</span>) {
        customAuth.authenticate = strategy;
    }
}

<span class="hljs-built_in">module</span>.exports = fp(customAuth);
</code></pre>
<p>As suggested by <a target="_blank" href="https://www.fastify.io/docs/latest/Reference/Decorators/">Fastify's Docs</a>, <code>decorateRequest</code> is used to allow the underlying JavaScript engine to optimize the handling of the objects sent in the context of requests. Since we modify the request's <code>user</code>, <code>isAuthenticated</code>, <code>logIn</code> and <code>logOut</code> objects in our custom auth module, I decorate those on the request object.</p>
<p>The <code>onRequest</code> hook (<code>fastify.addHook("onRequest"...</code>) is where our custom authentication's value comes in. <a target="_blank" href="https://www.fastify.io/docs/latest/Reference/Hooks/">Fastify Hooks</a> allow you to listen to specific events in the application, and we can use the <code>onRequest</code> hook to intercept each incoming request and modify it accordingly, before passing it on to the rest of our application to manage.</p>
<p>When each request is received, our custom auth checks the request's session user property to determine whether a value exists (<code>getUser</code> returns the <code>user</code>) or not (<code>getUser</code> returns <code>null</code>).</p>
<p>Afterward, we decorate the request with an <code>isAuthenticated</code> function that can be used in any later API handlers.</p>
<p>Then, we also add <code>logOut</code> and <code>logIn</code> functions that takes the applicable data and modifies the <code>session</code> with that authenticated user or not, so future requests will have that <code>session</code>'s data in them.</p>
<p>Finally, we populate the request's <code>user</code> property with the specific user, again for easy usage in a later API handler.</p>
<h4 id="heading-our-unopinionated-auth-system">Our unopinionated auth system</h4>
<p>Our custom auth focuses on setting cookies when a log in was successful, deleting cookies when a log out was called, and allowing later request handlers to get the requesting user's details. It does not determine <strong>how</strong> to authenticate the user. That is left up to the developer to decide.</p>
<p>For that, our custom auth exposes a <code>useStrategy</code> function, where the developer passes a stragety for authenticating a user. This is where the developer can check their database, compare encrypted passwords, and more. When successful, the developer can call the <code>done</code> function provided in the <code>logIn</code> method, which will authenticate the user and run the specific callback function (where the developer can redirect to another page, for example). If the first parameter is truthy, it will be considered an error, and that would mean the authentication failed. Otherwise, the authentication should be successful and the session's user property will be the data provided in the second parameter.</p>
<h3 id="heading-4-use-the-custom-auth-in-our-application">4) Use the custom auth in our application</h3>
<p>Now that we have created the module, let's import and use it in our <code>server.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">// server.js</span>
<span class="hljs-keyword">const</span> fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fastify"</span>);
<span class="hljs-keyword">const</span> fastifySecureSession = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@fastify/secure-session"</span>);
<span class="hljs-keyword">const</span> customAuth = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./customAuth"</span>); <span class="hljs-comment">// &lt; added line</span>

<span class="hljs-comment">// .. other code</span>

app.register(fastifySecureSession, secureSessionConfig);
app.register(customAuth); <span class="hljs-comment">// &lt; added line</span>
</code></pre>
<blockquote>
<p><strong>Important:</strong> Since our custom auth module relies on <code>req.session</code>, it is important to <code>register</code> fastify's secure session module <strong>before</strong> our custom auth module.</p>
</blockquote>
<p>Next, let's initialize our custom auth strategy before attempting to use it in our request handlers:</p>
<pre><code class="lang-js"><span class="hljs-comment">// server.js</span>

app.register(fastifySecureSession, secureSessionConfig);
app.register(customAuth); 

<span class="hljs-comment">// added chunk</span>
customAuth.useStrategy(<span class="hljs-keyword">async</span> (data, done) =&gt; {
    <span class="hljs-keyword">const</span> { username, password } = data;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> database.user.find({username, password}, {id, username, password});
        <span class="hljs-keyword">if</span> (!user)
            <span class="hljs-keyword">return</span> done(<span class="hljs-string">"User does not exist"</span>);

        <span class="hljs-keyword">if</span>(passwordVerifier.verify(password, user.password)) {
            <span class="hljs-keyword">return</span> done(<span class="hljs-literal">null</span>, { <span class="hljs-attr">id</span>: user.id, <span class="hljs-attr">username</span>: user.username });
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> done(<span class="hljs-string">"Username or password incorrect"</span>);
        }
    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> done(err);
    }
});

app.listen...
</code></pre>
<p>Our strategy will accept a username and password, and compare it to existing data in a database of our choice, then verify the matching passwords and call <code>done</code> based on whether the user was found with a matching password (user authenticated) or not (user not authenticated).</p>
<blockquote>
<p>For validating passwords, you can use packages like <code>bcrypt</code>.</p>
</blockquote>
<p>Finally, let's create some routes that uses our auth system:</p>
<pre><code class="lang-js"><span class="hljs-comment">// server.js</span>

app.post(<span class="hljs-string">"/signin"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed in"</span> });
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> { username, password } = req.body;

        req.logIn({ username, password }, <span class="hljs-function">(<span class="hljs-params">error, user</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (error) {
                res.send({ error });
            } <span class="hljs-keyword">else</span> {
                res.send({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, user });
            }
        });
    } <span class="hljs-keyword">catch</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Sign in failed"</span> });
    }
});

app.post(<span class="hljs-string">"/signup"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed up"</span> });
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// validate request</span>

        <span class="hljs-comment">// .. other code</span>
    } <span class="hljs-keyword">catch</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Sign up failed"</span> });
    }
});

app.delete(<span class="hljs-string">"/signout"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        req.logOut();
        res.send({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> });
    } <span class="hljs-keyword">else</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed out"</span> });
    }
});

app.listen...
</code></pre>
<p>Since these requests handlers are defined after the authentication module was imported, registered, and strategy initialized, each handler has access to our custom auth's decorated properties.</p>
<h4 id="heading-sign-in-code-explanation">Sign In Code Explanation</h4>
<p>In the sign in handler, we first check to see if the user is already logged in, using the decorated <code>req.isAuthenticated</code> method. Internally, the custom auth module checks the session properties for that existing cookie and returns <code>true</code> or <code>false</code> on whether it exists.</p>
<p>Then, we call the <code>req.logIn</code> method, passing the <code>username</code> and <code>password</code> we want to use in our authentication strategy we defined above. Internally, the custom auth module executes our authentication strategy, comparing the information to what we have in our database, just like we set it to.</p>
<p>In the <code>req.logIn</code> callback, we also handle the response provided by the internal <code>done</code> function; If no user exists, the <code>error</code> is sent to the user. Otherwise, a success response is sent, as the user is signed in. If the log in is successful, internally, the custom auth sets the session's user property to be that user, so that cookie value will be included in future requests, and the server will be able to determine that the user is authenticated correctly.</p>
<h4 id="heading-sign-up-code">Sign Up Code</h4>
<p>This one is left up to the developer's implementation, but note that I use the <code>req.isAuthenticated</code> method again as part of the request handling flow. In this case, logged in users are not authorized to create new accounts; we do not want to handle signing up a user when they are already logged in.</p>
<blockquote>
<p>Of course, depending on your application, you can omit this code block.</p>
</blockquote>
<h4 id="heading-sign-out-code-explanation">Sign Out Code Explanation</h4>
<p>This request handler is simple; it calls the decorated <code>req.logOut</code> method if the user is logged in. Internally, the custom auth module will delete the session's user properties, so the cookies won't exist on future requests.</p>
<h3 id="heading-tutorial-conclusion">Tutorial Conclusion</h3>
<p>There you have it! Your own custom authentication system in under 100 lines of code, that allows you to define your own authentication strategy, and manage authorization in your request handlers.</p>
<p>Here is the full code (and pseudocode) for review:</p>
<pre><code class="lang-js">server.js

<span class="hljs-keyword">const</span> fastify = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fastify"</span>);
<span class="hljs-keyword">const</span> fastifySecureSession = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@fastify/secure-session"</span>);
<span class="hljs-keyword">const</span> customAuth = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./customAuth"</span>);

<span class="hljs-keyword">const</span> secureSessionConfig = {
    <span class="hljs-attr">secret</span>: process.env.COOKIE_SECRET || <span class="hljs-string">"averylogphrasebiggerthanthirtytwochars"</span>,
    <span class="hljs-attr">cookie</span>: {
        <span class="hljs-attr">path</span>: <span class="hljs-string">"/"</span>
    }
}
<span class="hljs-keyword">if</span> (process.env.NODE_ENV === <span class="hljs-string">"production"</span>) {
    secureSessionConfig.cookie.secure = <span class="hljs-literal">true</span> <span class="hljs-comment">// serve secure cookies</span>
    secureSessionConfig.cookie.httpOnly = <span class="hljs-literal">true</span> <span class="hljs-comment">// serve cookies only accessible by the server</span>
    secureSessionConfig.cookie.sameSite = <span class="hljs-string">"strict"</span>; <span class="hljs-comment">// serve cookies only from site</span>
}

<span class="hljs-keyword">const</span> app = fastify({ <span class="hljs-attr">ignoreTrailingSlash</span>: <span class="hljs-literal">true</span> });

app.register(fastifySecureSession, secureSessionConfig);
app.register(customAuth);

customAuth.useStrategy(<span class="hljs-keyword">async</span> (data, done) =&gt; {
    <span class="hljs-keyword">const</span> { username, password } = data;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> database.user.find({ username, password }, { id, username, password });
        <span class="hljs-keyword">if</span> (!user)
            <span class="hljs-keyword">return</span> done(<span class="hljs-string">"User does not exist"</span>);

        <span class="hljs-keyword">if</span> (passwordVerifier.verify(password, user.password)) {
            <span class="hljs-keyword">return</span> done(<span class="hljs-literal">null</span>, { <span class="hljs-attr">id</span>: user.id, <span class="hljs-attr">username</span>: user.username });
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> done(<span class="hljs-string">"Username or password incorrect"</span>);
        }
    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> done(err);
    }
});

app.post(<span class="hljs-string">"/signin"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed in"</span> });
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> { username, password } = req.body;

        req.logIn({ username, password }, <span class="hljs-function">(<span class="hljs-params">error, user</span>) =&gt;</span> {
            <span class="hljs-keyword">if</span> (error) {
                res.send({ error });
            } <span class="hljs-keyword">else</span> {
                res.send({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, user });
            }
        });
    } <span class="hljs-keyword">catch</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Sign in failed"</span> });
    }
});

app.post(<span class="hljs-string">"/signup"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed up"</span> });
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// validate request</span>

        <span class="hljs-comment">// .. other code</span>
    } <span class="hljs-keyword">catch</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Sign up failed"</span> });
    }
});

app.delete(<span class="hljs-string">"/signout"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (req.isAuthenticated()) {
        req.logOut();
        res.send({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span> });
    } <span class="hljs-keyword">else</span> {
        res.send({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Already signed out"</span> });
    }
});

app.listen({ <span class="hljs-attr">port</span>: process.env.PORT || <span class="hljs-number">3000</span> }, <span class="hljs-function">(<span class="hljs-params">err, address</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (err) {
        app.log.error(err);
        process.exit(<span class="hljs-number">1</span>);
    }
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server started on address "</span> + address);
});
</code></pre>
<pre><code class="lang-js"><span class="hljs-comment">//customAuth.js</span>

<span class="hljs-keyword">const</span> fp = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fastify-plugin'</span>)
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">customAuth</span>(<span class="hljs-params">fastify, options</span>) </span>{

    fastify.decorateRequest(<span class="hljs-string">"user"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"isAuthenticated"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"logIn"</span>, <span class="hljs-literal">null</span>);
    fastify.decorateRequest(<span class="hljs-string">"logOut"</span>, <span class="hljs-literal">null</span>);

    fastify.addHook(<span class="hljs-string">"onRequest"</span>, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> getUser = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">const</span> user = req.session.get(<span class="hljs-string">"user"</span>);
                <span class="hljs-keyword">if</span> (user) {
                    <span class="hljs-keyword">return</span> user;
                }
                <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
            } <span class="hljs-keyword">catch</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
            }
        }
        req.isAuthenticated = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">return</span> !!getUser();
        }
        req.logOut = <span class="hljs-function">() =&gt;</span> {
            req.session.set(<span class="hljs-string">"user"</span>, <span class="hljs-literal">undefined</span>);
        }
        req.logIn = <span class="hljs-function">(<span class="hljs-params">data, cb</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> done = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">error, user</span>) </span>{
                <span class="hljs-keyword">if</span> (error) {
                    <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                        cb(error, user);
                    <span class="hljs-keyword">return</span>;
                }
                req.session.set(<span class="hljs-string">"user"</span>, user);
                <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                    cb(<span class="hljs-literal">null</span>, user);
            }
            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">if</span> (customAuth.authenticate &amp;&amp; <span class="hljs-keyword">typeof</span> customAuth.authenticate === <span class="hljs-string">"function"</span>) {
                    customAuth.authenticate(data, done);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                        cb(<span class="hljs-string">"Cannot authenticate. No authentication method found."</span>, <span class="hljs-literal">null</span>);
                }
            } <span class="hljs-keyword">catch</span> (err) {
                <span class="hljs-keyword">if</span> (cb &amp;&amp; <span class="hljs-keyword">typeof</span> cb === <span class="hljs-string">"function"</span>)
                    cb(err, <span class="hljs-literal">null</span>);
            }
        }
        req.user = getUser();
        next();
    });
}

customAuth.useStrategy = <span class="hljs-function">(<span class="hljs-params">strategy</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (strategy &amp;&amp; <span class="hljs-keyword">typeof</span> strategy === <span class="hljs-string">"function"</span>) {
        customAuth.authenticate = strategy;
    }
}

<span class="hljs-built_in">module</span>.exports = fp(customAuth);
</code></pre>
<p>And that's it! Now you know how to implement custom authentication in your own application!</p>
<blockquote>
<p><strong>Trivia:</strong> If you are familiar with Passport, many of the exposed decorated functions may look similar, and that is because...again...most of my prior experience with auth is using Passport and its local strategy.</p>
</blockquote>
<p>Some topics were not covered in the tutorial, such as hiding sensitive environment variables (using packages like <code>dotenv</code>), or other best practices, because that was not the focus of the tutorial. However, do be sure to follow such best practices in your own projects.</p>
<h4 id="heading-theory-refresher">Theory Refresher</h4>
<p>Auth is about determining who made the request, and if that person is allowed to perform an action or not. We authenticate our users by storing their ID in a <code>secure</code>, <code>httpOnly</code> cookie, signed with a <code>secret</code>, and <code>CSRF</code> mitigated with a <code>"lax"</code> or <code>"strict"</code> <code>sameSite</code> value. Then, we created an unopinionated authentication system that accesses and modifies those cookies for us, and provides functions for us to use in our request handlers to determine <em>how</em> to authenticate <strong>and</strong> authorize users ourselves, no matter what databases or flow we use.</p>
<p>The benefit of this theory is that, even if you are using another NodeJS web framework (such as Express) or another language entirely, you'll be able to follow along and create your own authentication system.</p>
<h3 id="heading-assurances">Assurances</h3>
<p>As a developer that switched from primarily using an authentication library to creating one myself, you may wonder whether my custom implementation may have flaws the existing libraries covers/fixes?</p>
<p>However, before attempting this implementation, I had 2 years prior of implementing authentication in different applications, where I mostly used PassportJS. For this library, I took about 2 weeks researching the topic, which involved reading blogs, articles, and documentation on authentication, discussing it with Discord peers, watching <a target="_blank" href="https://www.youtube.com/watch?v=h6wBYWWdyYQ">YouTube Videos</a>, and more. I also took a look at the source code of the <a target="_blank" href="https://github.com/fastify/fastify-passport/tree/main/src">@fastify/passport</a> module, and surprisingly enough, it is very similar to how I implemented auth myself. Thus, I am a bit certain about what I've created.</p>
<blockquote>
<p>And if I am completely wrong, please feel free to educate me! 🙏🏾</p>
</blockquote>
<h3 id="heading-disclaimers">Disclaimers</h3>
<p>Authentication always seems easy, but there are a few gotchas that have terrible consequences if you are not aware of them. It is possible that I have not covered all cases either, so please let me know in the comments.</p>
<p>While <a class="post-section-overview" href="#heading-assurances">assured</a>, this code has not been battle-tested in a production environment for robustness. I encourage you to do your own additional research before creating your own authentication systems as well. For that, I generally recommend reading the various <a target="_blank" href="https://cheatsheetseries.owasp.org/">OWASP resources</a>. There are many more layers of defense that can be implemented (such as <code>CSRF Tokens</code>) to protect users in a more robust way, and such resources can help guide you.</p>
<blockquote>
<p>Alternatively, I encourage you to use an existing battle-tested auth library instead.</p>
</blockquote>
<h1 id="heading-conclusion">Conclusion</h1>
<p>After implementing authentication of my own, I was able to continue working on Projectree's new backend. Since I decided to learn new tools, I spent about a month rewriting the backend to bring it up to the current Django version. The results from the hackathon have already been released; I no longer need to keep it on PlanetScale's databases, so I've deployed it on <a target="_blank" href="https://www.linode.com/">Linode</a>.</p>
<blockquote>
<p>Unfortunately, our project did not receive a winning prize in the hackathon, so when Heroku's free dyno tiers end, we will not have the funds to support it on a paid tier anyways, so the change in deployment was the more convenient decision.</p>
</blockquote>
<p>I plan to continue working on Projectree in the future, so feel free to <a target="_blank" href="https://projectree.net">check it out and use it!</a>.</p>
<p>Stay tuned throughout the next 2 weeks for more articles like this!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[How To Solve Bugs During Development]]></title><description><![CDATA[Solving Errors When Stuck
To many developers (like me), software development involves brainstorming problems, implementing solutions, and of course, debugging errors. Inevitably, there may be a breaking change that needs fixing, and sometimes, system...]]></description><link>https://blog.danidre.com/how-to-solve-bugs-during-development</link><guid isPermaLink="true">https://blog.danidre.com/how-to-solve-bugs-during-development</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[#week2]]></category><category><![CDATA[problem solving skills]]></category><category><![CDATA[debugging]]></category><category><![CDATA[research]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Mon, 29 Aug 2022 01:15:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661732644620/x-4-WyQ6L.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-solving-errors-when-stuck">Solving Errors When Stuck</h1>
<p>To many developers <em>(like me)</em>, software development involves brainstorming problems, implementing solutions, and of course, debugging errors. Inevitably, there may be a breaking change that needs fixing, and sometimes, systems crash entirely, because the developer is unable to find the errors or debug the issue.</p>
<p>Here are the <strong>top 4</strong> things I do when stuck during development:</p>
<h2 id="heading-1-retracing-my-steps">1. Retracing my steps</h2>
<p>I always check the error messages to see what broke. In frontend development, I use the browser's developer tools to monitor the logged errors. In backend development, the terminal generally has the error messages. Most times, the error message tells you exactly what went wrong.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661733210850/cB1UjZFMa.JPG" alt="dev console error.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>Browser error message, telling me I've made a syntax error.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661733451528/NiXJzLtRK.JPG" alt="terminal error.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>Terminal error message, telling me a variable is not defined.</p>
</blockquote>
<p>Many times, I spelt a variable incorrectly (<code>fooo</code> instead of <code>foo</code>), or missed a semi-colon at the end of the line. These are <strong>syntax errors</strong>, and can easily be solved when identified; and I can continue development quickly.</p>
<h2 id="heading-2-researching-the-issue-documentation-stackoverflow-forums-etc">2. Researching the issue (documentation, stackoverflow, forums, etc)</h2>
<p>Oftentimes, the incorrect use of imported libraries can cause the errors. Sometimes, a simple quick fix is not readily available, as the developer does not know how to use the library's APIs correctly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661733775594/S68ck_06j.JPG" alt="error from library.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>Error trying to find all records using <code>prisma</code>.</p>
</blockquote>
<p>When I'm stuck with errors from an API, I google search the library or package and browse multiple links on the topic. For example, if I forgot the proper syntax for creating a record in prisma, I enter the following search:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661733808693/TRWLUFyGn.JPG" alt="create user prisma search.JPG" class="image--center mx-auto" /></p>
<p>From there, I check the documentation, Stack Overflow discussions, or GitHub forums for proper references and guidance. Most times I leave with much more knowledge than I was looking for, such as how to create a record <strong>and</strong> update it too.</p>
<p>I also use google when searching for algorithms or other functions I may need, that are too complex to retype every time. Maybe I already implemented a <code>remainder</code> function but realize that it moves slow. Googling existing <code>remainder</code> functions in the language of my choice can bring a much more optimized code snippet that I can use to speed up my own application.</p>
<h4 id="heading-my-first-ever-implementation-of-a-remainder-function">My first ever implementation of a remainder function:</h4>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">remainder</span>(<span class="hljs-params">dividend, divisor</span>) </span>{
    <span class="hljs-keyword">if</span>(dividend == divisor) {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(divisor == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> divisor;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(dividend == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(dividend &gt; <span class="hljs-number">0</span>) {
        divisor = <span class="hljs-built_in">Math</span>.abs(divisor);
        <span class="hljs-keyword">while</span>(dividend &gt;= divisor) {
            dividend -= divisor;
        }
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(dividend &lt; <span class="hljs-number">0</span>) {
        divisor = <span class="hljs-built_in">Math</span>.abs(divisor) * <span class="hljs-number">-1</span>;
        <span class="hljs-keyword">while</span>(dividend &lt;= divisor) {
            dividend -= divisor;
        }
    }
    <span class="hljs-keyword">return</span> dividend;
}
</code></pre>
<h3 id="heading-a-similar-function-found-while-searching">A similar function found while searching:</h3>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">remainder</span>(<span class="hljs-params">dividend, divisor</span>) </span>{
    <span class="hljs-keyword">return</span> dividend % divisor;
}
</code></pre>
<blockquote>
<p>Of course, I <strong>"generally"</strong> did the research to understand the optimized code before I used it in my own work.</p>
</blockquote>
<h2 id="heading-3-manually-debugging-code">3. Manually debugging code</h2>
<p>Sometimes, even without syntax errors, your program would not work as expected. Either your function to perform a calculation returns a <code>NaN</code> or <code>Infinity</code> value, you are redirected to an arbitrary page, or your application generates additional fields unexpectedly because you put <code>&lt;=</code> instead of <code>&lt;</code> in a for loop. These can all be caused by <code>logic errors</code> or <code>runtime errors</code>.</p>
<p>When researching or reading error logs do not work, I add breakpoints to my code to interactively see the different values to understand where it went wrong. Can you determine why my add function returns <code>20</code> instead of <code>9</code> when I input <code>4</code> and <code>5</code>?</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTwoNumbers</span>(<span class="hljs-params">num1, num2</span>) </span>{
    <span class="hljs-keyword">let</span> sum = num1 * num2;
    <span class="hljs-keyword">return</span> sum;
}
</code></pre>
<p>If you realized I put the wrong operator, you guessed correctly. It did not give syntax errors, but the application did not work as intended. Adding breakpoints and then monitoring each line can help find logic errors and get your functions working again, so you are no longer stuck.</p>
<h3 id="heading-debugging-trivia-shameful-disclaimer">Debugging Trivia (Shameful Disclaimer)</h3>
<p>While I do use breakpoints, I find it easier <em>(and faster)</em> to log my variables in the function and then monitor the console for the specific values:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addTwoNumbers</span>(<span class="hljs-params">num1, num2</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Num1 is "</span> + num1);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Num2 is "</span> + num2);

    <span class="hljs-keyword">let</span> sum = num1 * num2;

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Sum is "</span> + sum);

    <span class="hljs-keyword">return</span> sum;
}

<span class="hljs-keyword">let</span> nine = addTwoNumbers(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>)

<span class="hljs-comment">// Console prints:</span>
<span class="hljs-comment">// &gt; Num1 is 4</span>
<span class="hljs-comment">// &gt; Num2 is 5</span>
<span class="hljs-comment">// &gt; Sum is 20</span>
</code></pre>
<p>From the console, I notice the sum was incorrect, which prompts me to check <code>sum</code> assignment expression and realize I wrote <code>*</code> instead of <code>+</code> in the statement.</p>
<h2 id="heading-4-exploring-communities-like-discord">4. Exploring communities like Discord</h2>
<p>When learning to develop, I joined Discord servers such as <a target="_blank" href="https://discord.gg/7StTjnR">Web Dev Simplified</a> and <a target="_blank" href="https://discord.gg/vUsrbjd">Nodeiflux</a> and spent lots of time asking questions there. Many helpful developers assisted in real time debugging and problem solving.</p>
<h3 id="heading-disclaimer-being-spoonfed">Disclaimer (Being Spoonfed)</h3>
<p>While it is helpful to have instantaneous assistance, try to use this option as a last resort. Making mistakes and learning how to solve them is important to a developer's growth and learning. At the beginning I spammed questions for every small issue, but soon realized that I would only depend on others, and not learn anything myself, so I tried solving problems myself before asking for an answer right away. I have actually been able to solve the issues myself without needing assistance!</p>
<blockquote>
<p>As time passed, I have been able to give back to these communities by answering questions myself, helping other developers that may be stuck themselves.</p>
</blockquote>
<hr />
<h1 id="heading-minimizing-errors-and-increasing-development-productivity">Minimizing Errors and Increasing Development Productivity</h1>
<p>During development, causing bugs or getting stuck is inevitable. However, with the right tools, sufficient practice, and patient preparation, you can reduce the amount of bugs that may appear in your code, and get stuck less. That way, you can spend more time working on your applications, and less time debugging or being stuck.</p>
<p>Here are 2 tips for decreasing the chances of getting stuck while you code:</p>
<h2 id="heading-1-spend-time-brainstorming-your-problem-before-implementing-it">1. Spend time brainstorming your problem before implementing it</h2>
<p>An important part of development is design; understanding what you are developing as well as what is needed for it. Generally, I spend more time thinking about how I will implement a feature, than I spend actually implementing that feature.</p>
<h3 id="heading-discuss-it-with-peers">Discuss it with peers</h3>
<p>Usually, I request feedback on proposed solutions by asking classmates, talking to discord helpers, or publishing discussions on forums. Sometimes, they share knowledge that benefits me; whether it is a different perspective, a more efficient function, or an existing tool that already solves my problem.</p>
<h3 id="heading-write-it-down-on-paper">Write it down on paper</h3>
<p>I also like breaking down steps on paper to mockup what needs to be achieved, often with pseudocode to help. For my <a target="_blank" href="https://flevar.com/">game engine</a>, I often spent weeks designing or constructing an idea or feature, and then when it was time to implement it, I did so in a few days.</p>
<p>The benefit of planning in advance is that you have enough time to think of many general edge cases, and when you get to coding, you implement the feature faster. Also, since you are may already have pseudocode, you can end up with less errors, as you are not instantly coding from your head and thinking of fixing unexpected cases afterward.</p>
<blockquote>
<p>Spending lots of time planning first before implementing is not suitable for all development environments; you still spend time on the application. However, that time is spent productively designing and developing, rather than developing and then trying to debug and fix crashes and errors.</p>
</blockquote>
<h2 id="heading-2-use-statically-typed-languages">2. Use statically typed languages</h2>
<p>With dynamically typed languages (such as JavaScript or Python), type checking occurs at runtime. It is easy to write logically incorrect code, such as attempting to subtract <code>objects</code> from <code>integers</code>. While these assignments may not result in syntax errors all the time, they do result in logical errors that are only discovered at runtime. </p>
<p>With statically typed languages (such as TypeScript or C++), type checking occurs at compile time. The benefit of this is that you are warned in advance of any possible unintended expressions that can result in hard to discover bugs. This increases productivity as well, since less time is spent finding bugs later on.</p>
<h1 id="heading-bonus-advice">Bonus Advice</h1>
<h2 id="heading-it-is-okay-to-fail">It is okay to fail</h2>
<p>Even with all those steps and tips, you can still get stuck: </p>
<ul>
<li>maybe the error message is not helpful at all;</li>
<li>maybe google has no searches recorded for the issue;</li>
<li>maybe debugging code and creating breakpoints shows no abnormalities;</li>
<li>maybe no one on Discord can help you with your issue either;</li>
<li>maybe after days preparing your solution, implementation fails, and you have to re-plan everything;</li>
</ul>
<p>That is okay, and to that I suggest taking a break, and trying again at another time. Maybe you may be able to solve it in the future.</p>
<blockquote>
<p>In production environments where deadlines are impending, professional advice would be to reach out to a senior for assistance; it is better to admit your shortcomings and seek assistance earlier, than keeping quiet until deadlines where everything is in a rush.</p>
</blockquote>
<h1 id="heading-conclusion">Conclusion</h1>
<p>These are all steps that have continued to work for me as a software developer. Of course, it gets frustrating when you are stuck, and I blame the software occasionally, but eventually, I have always been able to solve my problems and proceed with my software development.</p>
<blockquote>
<p>It is always a <strong>refreshing thrill</strong> when your code finally works again!</p>
</blockquote>
<p>Stay tuned throughout the next 3 weeks for more articles like this!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Why You Should Be a Software Developer]]></title><description><![CDATA[Introduction
Many people see software development as an avenue to easy money. Lately, with the drive of "anyone can code" promoted by major companies, this holds true even more. People run with this naive misconception that they can attend a 4-6 mont...]]></description><link>https://blog.danidre.com/why-you-should-be-a-software-developer</link><guid isPermaLink="true">https://blog.danidre.com/why-you-should-be-a-software-developer</guid><category><![CDATA[4articles4weeks]]></category><category><![CDATA[week1]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Sun, 21 Aug 2022 21:51:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1661117948789/xbB1GOnQg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Many people see software development as an avenue to easy money. Lately, with the drive of "anyone can code" promoted by major companies, this holds true even more. People run with this naive misconception that they can attend a 4-6 month coding bootcamp and then land a 6 figure job.</p>
<blockquote>
<p>And sometimes, this holds true, and I'm always amazed by such people.</p>
</blockquote>
<p>But oftentimes, I witness people entering discords and asking questions because they just completed a course and landed a job, but do not know how to accomplish what is being asked, and fear they will get fired soon.</p>
<blockquote>
<p>Impostor syndrome; it haunts me to this day.</p>
</blockquote>
<h1 id="heading-what-makes-a-software-developer">What makes a software developer</h1>
<p>I believe developers should have a strong love or passion for what they're doing. Of course, for some people, programming really is just a job they do while at an office. But many developers I look up to are always tinkering and dabbling away during their spare time, participating in discussions on the direction of software, working on the open source software themselves, and more. Many of them have beginner stories of what inspired them to become developers, which includes programming as a child, or wanting to learn how to make a game or service for themselves or a close one.</p>
<p>And my story is no different.</p>
<h1 id="heading-what-got-me-into-software-development">What got me into software development</h1>
<p>I always used to draw random games in primary school, and funny enough drew a 2D version of Minecraft in checkered-line books back in Standard 5 (grade 5).</p>
<blockquote>
<p>I do not remember if it was inspired by Minecraft of not, since that was in 2010.</p>
</blockquote>
<p>Fast forward to 2013 in Form 2 at secondary school (grade 7), my little bother found a 2D minecraft game called <a target="_blank" href="https://mineblocks.com/">Mineblocks</a> created by an amazing developer called <a target="_blank" href="https://zanzlanz.com/">Zanzlanz</a>. As a hardcore fan, I found an option to contact him and sent him spam mail about how I can go about creating my own games.</p>
<blockquote>
<p>Prior to this, I also did the same to Notch, to no avail. So I had no big hopes Zanzlanz would respond either.</p>
</blockquote>
<p>Fortunately, what followed was him linking me to his YouTube and me watching his "How to Create 2D Minecraft in Flash".</p>
<h2 id="heading-trying-development-myself">Trying Development Myself</h2>
<p>Before actually getting a response though, I remember being in the vehicle one day from school playing Mineblocks locally on my laptop and testing out GameMaker. I had tried the tutorial at the time, but at the end had no idea how to continue on my own. With Zanzlanz's tutorial on YouTube, what followed was a series of me googling "How to make racing game in flash", "How to make shooting game in flash"... basically "How to make x in flash". Furthermore, I even began googling more of "How to create 2D Minecraft" and saw a series in Java by Ulixava.</p>
<blockquote>
<p>As you can see, I was really obsessed with Minecraft clones.</p>
</blockquote>
<h2 id="heading-understanding-programming-at-last">Understanding Programming at Last</h2>
<p>What followed <em>(again)</em> was about a year or 2 of me copy pasting code from YouTube tutorials, first in flash, then in Java. One day, I returned to flash, looked at the code...and just...understood...what it did.</p>
<p>Since then I've branched off from game development alone to web development and (since Flash support ended) it was easy to adopt the JavaScript language for my games. Then recently, as I completed highschool and entered college, I branched off more into full stack development, first watching videos from a creator named Kyle on his <a target="_blank" href="https://www.youtube.com/c/WebDevSimplified">WebDevSimplified</a> YouTube channel, checking out courses on <a target="_blank" href="https://www.freecodecamp.org/">freecodecamp</a>, and much more.</p>
<h1 id="heading-how-i-continue-to-learn">How I Continue to Learn</h1>
<p>During learning and development, I joined Discord servers for assistance, and gradually became a member assisting others. Having to explain how/why something works to others, as well as debugging with them, has helped give me a greater understanding of things I may not have been able to figure out on my own.</p>
<p>Since then, I've also participated in hackathons, building open source software that can be useful to others, such as endpoint loggers, and game engines. That was when I realized, I prefer making tools that can be used to make creations. That was when I realized I may be a <strong>software architect</strong>.</p>
<p>Additionally, recently I became an intern, and although I only knew NodeJS, it had been fairly easy adopting PHP, Python, some Vue, and more.</p>
<h2 id="heading-sources-of-inspiration">Sources of Inspiration</h2>
<p>I'm always interested in how software works, and am inspired by those that build such software. For example, I was incredibly amazed by <a target="_blank" href="https://www.youtube.com/watch?v=O4IWJcafX8c">Tanney Linsley</a> in React Summit, 2022. He built react table over 5 years, adopting Typescript over 2 years, and now moved on to TanStack Table, a framework agnostic version of what he started building 5 years ago! </p>
<p>His passion and love for development consistently radiated through his spectacular presentation, which inspired me much more to one day be developing projects on quite a scale as his.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>It is completely valid to become a programmer, get a programming job, and aim for a 6-figure salary. However, software developers that love and have a passion for what they do, make remarkable contributions to everyone, regardless of salary.</p>
<p>I still have some years before I get to that level, with entering my final year in University, participating in more hackathons, or working on more hobby projects, but gradually I hope to contribute greatly to open source myself. One day, I hope to continue game development as well, since that is what I originally started with.</p>
<p>Stay tuned throughout the next 4 weeks for more articles like this!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Introducing: Projectree]]></title><description><![CDATA[Introduction
Projectree is a service that you can use to create simplified showcases of projects you have worked on.

Create and showcase your projects lists without the hassle of building it yourself. Just add your project details, choose a theme, a...]]></description><link>https://blog.danidre.com/introducing-projectree</link><guid isPermaLink="true">https://blog.danidre.com/introducing-projectree</guid><category><![CDATA[PlanetScale]]></category><category><![CDATA[PlanetScaleHackathon]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Collaboration]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Sun, 31 Jul 2022 04:59:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659324770386/Ba2GpWVg1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Projectree is a service that you can use to create simplified showcases of projects you have worked on.</p>
<blockquote>
<p>Create and showcase your projects lists without the hassle of building it yourself. Just add your project details, choose a theme, and generate!</p>
</blockquote>
<p>Creating a projectree is as simple as entering the information for each project, choosing a predefined theme, and generating the project! You can then download your generated projectree and host it on your own site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658943720648/IFtO1FOZz.gif" alt="creating_projectree.gif" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1658943733643/vGyoxuY9L.gif" alt="viewing_projectree.gif" /></p>
<p>Projectree was developed by <a target="_blank" href="https://hashnode.com/@Sophyia">Sophia</a> and <a target="_blank" href="https://hashnode.com/@Danidre">me (Danidre)</a> for the <a target="_blank" href="https://townhall.hashnode.com/planetscale-hackathon?source=danidre">PlanetScale x Hashnode Hackathon</a>, where the main criteria involved using PlanetScale as the database.</p>
<h2 id="heading-how-projectree-qualifies-for-the-hackathon">How Projectree Qualifies for the Hackathon</h2>
<p>What I described so far is a tool that lets developers create and download their mini project showcases. A simple tool, right? Thus, you may ask: "Where is the database involved?"</p>
<h3 id="heading-additional-features">Additional Features</h3>
<p>By default, Projectree can only create and edit one projectee for users on that browser. However, if developers want to save multiple projectrees (for different purposes), they can do so by creating an account. Signed in users have access to a dashboard overviewing all their projectrees, where they can tweak each individually.</p>
<h4 id="heading-even-more-features">Even More Features!</h4>
<p>With a Projectree account, developers can also host their projectrees directly on the website for others to view.</p>
<h3 id="heading-in-summary">In Summary</h3>
<p>User accounts, authentication, multiple projectrees, published projectrees... all these features require the database, which qualifies this project for the hackathon, as we use PlanetScale as this database.</p>
<h1 id="heading-features">Features</h1>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Tier</strong></td><td><strong>Signed Out</strong></td><td><strong>Registered User</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Generated Projectrees <br /> Available</strong></td><td>Unlimited</td><td>Unlimited</td></tr>
<tr>
<td><strong>Maximum Projectree <br /> Drafts Available</strong></td><td>1 <br /> <em>(overwrites previous information)</em></td><td>Unlimited  <br /> <em>(create and edit any amount)</em></td></tr>
<tr>
<td><strong>Hosting Plans</strong></td><td>Not available <br /> <em>(only downloading available)</em></td><td>Unlimited</td></tr>
</tbody>
</table>
</div><h2 id="heading-creating-a-projectree">Creating a Projectree</h2>
<p>To create a projectree, a developer can navigate to the <code>/create</code> route and begin editing. There, they can enter the details of their projectree, add project items, and customize those items, entering the project's name, description, languages used, source repository link, and demo site link.</p>
<p>Additionally, developers can add photos to the projectree by pasting existing links of photos.</p>
<blockquote>
<p>There are plans on the <a class="post-section-overview" href="#heading-development-roadmap">roadmap</a> to allow developers to upload their own photos, hosting them on cloudinary or uploadcare.</p>
</blockquote>
<p>After the developer enters the data, they can choose an existing theme from which the projectree will be styled and generated.</p>
<h1 id="heading-target-audience">Target Audience</h1>
<p>While doing <a target="_blank" href="https://www.reddit.com/r/javascript/comments/vq3uh3/askjs_are_there_any_portfolio_or_projects_list/">market research</a> on the need for such a project, a FANG interviewer said that they would instantly skip a generated portfolio for a frontend position. I also believe that frontend developers/designers should develop their own portfolios and take the opportunity to showcase their skills.</p>
<h2 id="heading-benefits-for-developers">Benefits for developers</h2>
<p>Projectree may be most useful for fullstack, backend, devops, or other relevant positions where the focus of their work is not on the display of their design skills. If they still want a tool to quickly showcase their projects (without having to click into every GitHub repository, for example), Projectree can help. The showcase can display what the project is about, before checking out the repositories, allowing the viewer to decide whether to look further on any one project that catches their eye.</p>
<h1 id="heading-built-with">Built With</h1>
<p>Projectree is a tool that allows developers to create project showcases, host it for them, or allow them to download it and host it elsewhere. Seems like a pretty simple application, right? However, it's not!</p>
<p>While accustomed to full stack development, I generally despise the database and deployment side of development. Thus, I partnered with <a target="_blank" href="https://hashnode.com/@Sophyia">Sophia</a> for this hackathon. The agreement was that Sophia would work on the backend, connect to the database, deploy it, and provide APIs for me to consume on the frontend.</p>
<h2 id="heading-frontend-stack-danidre">Frontend Stack (Danidre)</h2>
<h3 id="heading-langauges-and-libraries-used">Langauges and libraries used</h3>
<ul>
<li>HTML</li>
<li>CSS (using Twind; for styling)</li>
<li>JavaScript (using DCL)</li>
<li>Netlify (for hosting)</li>
</ul>
<h3 id="heading-why-i-used-them">Why I used them</h3>
<p>I just planned to create something <em>simple</em>; nothing complex. I just needed some HTML, CSS, and JavaScript, and would use many <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">fetch</a> requests to the backend. But I would not be serving multiple views from the server, as everything would be client side. However, I still wanted the flexibility of reusing multiple components (like navigation bars and footers) without duplicating the code on separate frontend pages.</p>
<p>Additionally, I could not choose between React, Svelte, Solid, Vue, Handlebars, Pug, or other libraries and frameworks. React and other libraries come with the complication of requiring build tools or bundlers to compile or transpile code. I just wanted something I could drag and drop into Netlify, and it would work! Thus, I decided to create a library myself!</p>
<blockquote>
<p>Because who gives up on an opportunity to learn something new!</p>
</blockquote>
<p>I called it DCL, short for Danidre's Client Library (for lack of better name) and it has its own routing system, component nesting, event listeners, global states, and more.</p>
<p>All in all I probably spent 60% of the frontend work developing DCL, and the other 40% developing the frontend for Projectree, using DCL.</p>
<blockquote>
<p>A lot of nights were spent teeth grinding and bug solving DCL errors that made me wish more and more than I used an existing framework...but... did it for the learning experience 😌</p>
</blockquote>
<p>For styling, I used <a target="_blank" href="https://twind.dev/">Twind</a>, the smallest, fastest, most feature complete tailwind-in-js solution in existence. With this, I did not need PostCSS (which includes a build-step) which benefitted the Netlify drag and drop model I wanted.</p>
<h3 id="heading-in-summary">In Summary</h3>
<p>I wanted something simple; nothing complex. I did not need React or anything else that required bundlers and extra steps. In the end I created a mini library that caused headaches throughout development, but I'm really proud of what it turned out to be.</p>
<blockquote>
<p>There is no official public repository dedicated to DCL yet, but you can check it out in <a target="_blank" href="https://github.com/danidre14/projectree-frontend/tree/main/static/js/DCL">Projectree's source code</a>.</p>
</blockquote>
<h2 id="heading-backend-stack-sophia">Backend Stack (Sophia)</h2>
<h3 id="heading-langauges-and-tools-used">Langauges and tools used</h3>
<ul>
<li>Python (using Django &amp; DjangoRest Framework; for API)</li>
<li>MySQL (using PlanetScale; for database)</li>
<li>Heroku (for deployment)</li>
</ul>
<h3 id="heading-why-i-used-them">Why I used them</h3>
<p>The backend structure is built using Django and Django rest framework. I used the framework to build the API (Application Program Interface) that connects the frontend to the database. I used Django because it is robust and easy to use; in addition, every API built using Django is very secure.</p>
<p>Since Projectree is a simple web app, it was easy for me to build and secure users' data. I chose Django because building API with its framework is easy and creating secured APIs and optimizing the database using Django was smooth for me. </p>
<p>The user authentication system on the backend is built based on <a target="_blank" href="https://django-rest-framework-simplejwt.readthedocs.io/">JWT Authenticatication</a> and each user uses a bearer token to create, edit, delete and view projectrees, project items, and published projects. Once the token expires, the user is logged out and must log in again to continue accessing advanced services on Projectree.</p>
<p>Building this project gave me a better insight into the authentication and authorization of Django and its framework. I talk about authentication because without that, the backend system and database would be exposed to malicious users. I believe security is the major thing to consider when building a backend system. Always validate. Never just trust the client.</p>
<h3 id="heading-in-summary">In Summary</h3>
<p>It was an exciting journey learning about how Django works and the way authentication and APIs work. Django being robust, SEO optimized, rapid, easily scalable, and versatile were my main reasons for choosing the stack for this project. </p>
<h1 id="heading-development-roadmap">Development Roadmap</h1>
<p>Projectree aims to be simple enough to use that a developer can create their project showcase in as little as 5 minutes. For that, the features prioritized were minimal proofs of concept, with future plans for features than can enhance the user experience of the service:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Icon</strong></td><td><strong>Meaning</strong></td></tr>
</thead>
<tbody>
<tr>
<td>💻</td><td>Frontend</td></tr>
<tr>
<td>🗄️</td><td>Backend</td></tr>
<tr>
<td>⚙️</td><td>Both Frontend and Backend</td></tr>
</tbody>
</table>
</div><ul>
<li><p>[x] <strong>v 0.1</strong></p>
<ul>
<li>[x] 💻 Set up a single page application</li>
<li>[x] 💻 Develop custom frontend library</li>
<li>[x] 🗄️ Set up a django application</li>
<li>[x] ⚙️ Add readmes and licenses</li>
<li>[x] ⚙️ Determine database models and schemas</li>
<li>[x] ⚙️ Plan API routes</li>
<li>[x] ⚙️ Set up github repos</li>
</ul>
</li>
<li><p>[x] <strong>v 0.2</strong></p>
<ul>
<li>[x] 💻 Make "Create Projectree" prototype page</li>
<li>[x] 💻 Develop a standard theme for projectrees</li>
<li>[x] 💻 Allow users to download generated projectree</li>
<li>[x] 🗄️ Set up local database</li>
<li>[x] 🗄️ Develop register/login authentication</li>
</ul>
</li>
<li><p>[x] <strong>v 0.3</strong></p>
<ul>
<li>[x] 💻 Create sign up, sign in, and dashboard pages</li>
<li>[x] 💻 Make "Create Projectree" prototype page</li>
<li>[x] 💻 Deny or allow access to pages based on user authorization</li>
<li>[x] 🗄️ Create projectree models</li>
<li>[x] 🗄️ Develop REST APIs for projectrees</li>
<li>[x] 🗄️ Add route validation based on user authorization</li>
</ul>
</li>
<li><p>[x] <strong>v 0.5</strong></p>
<ul>
<li>[x] 💻 Create and style landing page, 404 page, and remaining pages</li>
<li>[x] 💻 Host frontend on Netlify</li>
<li>[x] 🗄️ Connect to planetscale database</li>
<li>[x] 🗄️ Test all API routes locally</li>
<li>[x] 🗄️ Deploy backend to Heroku</li>
</ul>
</li>
<li><p>[x] <strong>v 0.6</strong></p>
<ul>
<li>[x] 💻 Add meta tags for SEO</li>
<li>[x] 💻 Create page to view published projectrees</li>
<li>[x] 💻 Add privacy policy and tos pages</li>
<li>[x] ⚙️ Connect frontend to backend</li>
<li>[x] 💻 Add more predefined themes users can choose for their projectrees</li>
</ul>
</li>
<li><p>[ ] <strong>v 0.7</strong></p>
<ul>
<li>[ ] 💻 Use modals for alerts, prompts and confirms instead of browser natives</li>
<li>[ ] 🗄️ Add email confirmation when registering</li>
<li>[ ] 💻 Allow users to upload their project images instead of using a link only</li>
<li>[ ] 💻 Add user profile dashboard</li>
<li>[ ] ⚙️ Allow users to reset passwords</li>
<li>[ ] 💻 Allow exporting and importing projectree drafts as json</li>
</ul>
</li>
</ul>
<h1 id="heading-challenges-faced">Challenges Faced</h1>
<p>While Projectree seems like a really simple project, the challenges we faced during development took us the entirety of the month to complete it!</p>
<h2 id="heading-frontend-challenges-danidre">Frontend Challenges (Danidre)</h2>
<p>I spent majority of the time working on DCL (Danidre's Client Library) for everything to work. Most of my challenges involved tweaking its source, fixing bugs that broke everything, and adding features that would make development easier for me. These include the following:</p>
<h3 id="heading-creating-a-router-for-pages">Creating a Router for Pages</h3>
<p>I originally started off with <a target="_blank" href="https://www.youtube.com/watch?v=6BozpmSjk-Y">this code on YouTube</a> to create an SPA (single page application) with just JavaScript. I made use of history push and pop state so that the page would not have to refresh on every link clicked.</p>
<h3 id="heading-creating-nested-components">Creating Nested Components</h3>
<p>Afterward, I modified the code to make everything a component. That way, they can render other components by looking for the returned html in each.</p>
<h3 id="heading-handling-state-changes">Handling State Changes</h3>
<p>When state changes in a component, it needs to rerender everything in it to reflect the updated state. Instead of using a Virtual DOM (like React), I opted to wrapping each component in a fragment div by default, with unique dataSet IDs to identify those components alone. Then, the classes and dataSets are passed from that wrapper div to the main element returned for that component.</p>
<p>When it is rerendered, the element is first found on the page by the dataSet ID, rerendered with the wrapper, and then swapped to the child again, where the wrapper is removed again. This way, the wrappers do not break the intended developer's html semantics, as they are used just for rerendering, and then removed.</p>
<h3 id="heading-handling-global-changes">Handling Global Changes</h3>
<p>Similar to React's useContext, DCL uses context in a publish/subscribe model, where components can monitor changes of specific context values from the global application, and rerender based on that global change.</p>
<h3 id="heading-handling-focused-inputs">Handling Focused Inputs</h3>
<p>React has onchange handlers for input components that set the state as it is entered by the keyboard. However, when the components are then rerendered in DCL, the selected component is deleted and recreated, thus focus is lost. To regain the illusion of focus, DCL "remembers" the last component selected using the html's XPath, and after rerendering, tries its best to find and focus that element again. It also remembers the point in the input element that was selected, and reselects it, maintaining the focused element even when input changes.</p>
<h3 id="heading-preventing-leaving-unsaved-changes">Preventing Leaving Unsaved Changes</h3>
<p>DCL also has a <code>beforeNavigate</code> event that the developer can use to prevent the page from being left (or reloaded) if data is not yet saved.</p>
<h3 id="heading-ignoring-certain-routes">Ignoring Certain Routes</h3>
<p>DCL also has an <code>ignoreRoute</code> method that can be called inside any component to skip rendering that page. For example, if a logged out user tries to navigate to the dashboard, the route will be ignored and the <strong>Error 404</strong> page will appear instead.</p>
<h3 id="heading-in-summary">In Summary</h3>
<p>All these features were worked on side by side with the development of Projectree itself. During each hurdle, I wished more and more that I used an existing library that already solved these challenges, but in the end I am very proud of what I managed to create, and how fast it works!</p>
<blockquote>
<p>It is plain JavaScript, after all.</p>
</blockquote>
<h2 id="heading-backend-challenges-sophia">Backend Challenges (Sophia)</h2>
<p>I spent my time sketching the database (DB) schemas and thinking of the best way to design the database so that requests are seamless and fast. Unfortunately, I had lots of bumps on this development road and had lots of challenges fixing bugs. Deploying the backend with PlanetScale was the most challenging:</p>
<h3 id="heading-pushing-to-planetscale-db">Pushing to PlanetScale DB</h3>
<p>After extensive development, I finally realized PlanetScale DB does not support foreign-key constraints. It was a bit difficult for me to return to the drawing board and re-design everything. After that, deploying the backend to Heroku took me a week to solve because Heroku could not recognize the <code>django_psbd_engine</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659304859509/ZB2hT-OuA.png" alt="django_psdb_engine not available.png" /></p>
<p>I had to clone another open source project to help me understand what I did wrong and how I can resolve that. In the end, the error was caused because I did not set the wrapper correctly around the database settings in the backend's <code>settings.py</code> file.</p>
<blockquote>
<p>Thanks to Brian from the Developer Education team at PlanetScale for his support and assistance in deploying to Heroku.</p>
</blockquote>
<h3 id="heading-building-a-custom-user-system">Building a Custom User System</h3>
<p>When <a target="_blank" href="https://hashnode.com/@Danidre">Danidre</a> brought up this idea, I made it a mission to build a custom user authentication system rather than using Django's packages such as <a target="_blank" href="https://django-allauth.readthedocs.io/en/latest/installation.html">django-allauth</a>, or <a target="_blank" href="https://djangorest-routes.digitalstade.com/">djangrest-routes</a>. </p>
<p>It was a bit difficult because I had no idea what to do or where to start, so I read some articles and watched videos explaining how to start building a custom user model and securing the model using authentication and authorization. I finally got it right the third day!</p>
<blockquote>
<p>...and then came the errors and bugs...</p>
</blockquote>
<h3 id="heading-in-summary">In Summary</h3>
<p>All features are currently working successfully! It may not be as professional as full fledged APIs in enterprise projects, but I loved the experience, the thrills I got, and the excitement I got from every fixed bug or error. In the end, I am super proud of the system I built and its level of security; sometimes I can't believe I wrote this!</p>
<h1 id="heading-running-and-deployment">Running and Deployment</h1>
<p>Check the <a target="_blank" href="https://github.com/danidre14/projectree-frontend#prerequisites">GitHub Documentation</a> on deploying a fork of Projectree.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Projectree aims to allow the quick creation of project showcases. We hope you find it useful!</p>
<p>See the links below to support us or check the project out yourself:</p>
<ul>
<li>GitHub: <a target="_blank" href="https://github.com/danidre14/projectree-frontend">Frontend</a> | <a target="_blank" href="https://github.com/Sophyia7/projectree-backend">Backend</a></li>
<li>Application Link: https://projectree.net</li>
<li>Twitter: <a target="_blank" href="https://twitter.com/danidre">@danidre</a> | <a target="_blank" href="https://twitter.com/sophiairoegbu_/">@sophiairoegbu_</a></li>
<li>Patreon: <a target="_blank" href="https://www.patreon.com/danidre">Danidre</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Making 10 Minute Endpoint]]></title><description><![CDATA[Introduction
After participating in my first Write-a-thon, I proceeded with development as usual. One day, Hashnode announced another Hackathon, this time partnering with Linode, a cloud hosting company that provides virtual private servers.
The chal...]]></description><link>https://blog.danidre.com/making-10-minute-endpoint</link><guid isPermaLink="true">https://blog.danidre.com/making-10-minute-endpoint</guid><category><![CDATA[Linode]]></category><category><![CDATA[Linode Hackathon]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Devblog]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Fri, 01 Jul 2022 00:06:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656719618856/XFbKG8ucQ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>After participating in my first <a target="_blank" href="https://townhall.hashnode.com/the-epic-hashnode-writeathon">Write-a-thon</a>, I proceeded with development as usual. One day, <a target="_blank" href="https://hashnode.com/">Hashnode</a> announced another Hackathon, this time partnering with <a target="_blank" href="https://www.linode.com/">Linode</a>, a cloud hosting company that provides virtual private servers.</p>
<p>The challenge was to create an open source project using Linode in some way or another, and attribute both Hashnode and Linode in the project.</p>
<p>At first, I wanted to dismiss the challenge as out of my scope. Learning new technologies always brings you back to a level of inferiority when things don't work, especially when you get by just fine with the tools you're already accustomed to. Even when I checked out their website, I was overwhelmed by everything they provide: <a target="_blank" href="https://www.linode.com/linode-for-higher-education/">higher education</a>, <a target="_blank" href="https://www.linode.com/products/ddos/">ddos protection</a>, <a target="_blank" href="https://www.linode.com/linode-for-saas/">saas</a>...what did they all even mean??</p>
<p>I spent a day reading articles and watching <a target="_blank" href="https://www.youtube.com/watch?v=sD8X4CApdpo">YouTube</a> <a target="_blank" href="https://www.youtube.com/watch?v=KEK-ZxrGxMA">videos</a> on Linode, until I realized one thing I could use Linode for: deploying a NodeJS application. As a Windows developer, I mostly used services to deploy my apps (such as <a target="_blank" href="https://www.heroku.com/">Heroku</a> and <a target="_blank" href="https://caprover.com/">CapRover</a>). However, I recently gained experience with using Linux machines (Ubuntu 20.04) to manually deploy apps during my development at my summer internship.</p>
<p>Thus, all I needed to do was create an application locally, get a Linode virtual private server (vps), and deploy the app there.</p>
<blockquote>
<p>In this particular case, I definitely planned to refer to the YouTube videos for assistance when deploying.</p>
</blockquote>
<p>The only thing left to do was get an idea of an open source software to work on.</p>
<h1 id="heading-getting-an-idea">Getting an Idea</h1>
<p>Idea generation involves many steps, from market research, to brainstorming, to finding problems that you can create a solution to. I had been stuck on this for a while, until I found a problem during my internship that at the time I could not seem to solve.</p>
<h2 id="heading-having-a-problem">Having a Problem</h2>
<p>Have you ever needed to integrate software to a server, where you do not have access to the server logs, and no control over what the external API sends to the server? </p>
<p>In my case, the external service would send a request to the server with data when triggered by an external action. Unfortunately, I had no easy way of determining the values being sent in the request, because their documentation was vague. Additionally, I may have been able to replicate the server locally, but I had no clue how to broadcast my IP for the service to send the request there instead (nor did I want to do something potentially unsafe, security-wise).</p>
<h2 id="heading-finding-a-solution">Finding a Solution</h2>
<p>Thus, I wondered if there was a service one could use online to generate an endpoint for them, and they could use that in the external service. That way, when the requests are triggered, they can see what request was made and what data was sent. Unfortunately, I could not find any existing software to do it for me <em>at the time</em>, so I proceeded to make my own!</p>
<blockquote>
<p>Me being unable to find existing software was due to me not knowing what to search for at the time. During research and development of 10 Minute Endpoint, I realized I should have searched words like "webhooks", and found <a target="_blank" href="https://webhook.site/">Webhooks.site</a>, an existing service that already does what I needed (and also does so much more). However, I still decided to work on 10 Minute Endpoint for the learning experience. </p>
</blockquote>
<h1 id="heading-about-the-software">About the Software</h1>
<p>Much of the development behind 10 Minute Endpoint was influenced by design restrictions and time limits. I used this project to learn new tools, and I knew I would not be able to add all planned features within the month, so I minimised the goals of the project to remain within scope, and added additional features afterward.</p>
<h2 id="heading-what-is-10-minute-endpoint">What is 10 Minute Endpoint</h2>
<p><a target="_blank" href="https://10minuteendpoint.net/"><strong>10 Minute Endpoint</strong></a> is an open-source service that generates a temporary endpoint for a developer to test and inspect HTTP requests sent to it. This way, they can easily practice making HTTP (or fetch) requests by sending them to this url, or they can use this url in a third party service to easily see the data it sends to their endpoint, which can be useful when developing their APIs that may need certain fields of data.</p>
<h2 id="heading-how-it-got-its-name">How it got its name</h2>
<p>10 Minute Endpoint <strong>(10ME)</strong> was originally called Proxyen because of my lack of knowledge on the terminology. I knew of proxies that forwarded requests, and endpoints that managed requests, and mixed those words together. </p>
<p>Throughout further development, I realize that "proxies" was the wrong word to use. Additionally, to prevent overuse of a database <em>(that I don't know how to scale)</em>, I decided to follow existing services such as <a target="_blank" href="https://10minutemail.net/">10 Minute Mail</a> and only provide the endpoint for 10 minutes. After 10 minutes, the endpoint would be deleted, freeing space for other users.</p>
<h2 id="heading-whats-its-stack">What's its Stack</h2>
<p>10ME was developed with the PLEB stack:</p>
<ul>
<li>Database/storage with <strong>Prisma</strong> (MongoDB)</li>
<li>DevOps/deployed on <strong>Linode</strong></li>
<li>Backend/API with <strong>Express</strong> (NodeJS)</li>
<li>Frontend/design using <strong>BulmaCSS</strong></li>
</ul>
<h1 id="heading-how-10-minute-endpoint-works">How 10 Minute Endpoint works</h1>
<h2 id="heading-how-it-stores-data">How it stores data</h2>
<p>10 Minute Endpoint uses <a target="_blank" href="https://www.prisma.io/">Prisma</a> and MongoDB as its database.</p>
<h3 id="heading-why-i-chose-prisma">Why I chose Prisma</h3>
<p>I already had experience with applications made using MongoDB and PostgreSQL, but every time, the process of setting up the database, tables, schemas, etc was a tedious task.</p>
<p>Deploying with MongoDB was easier because I could use <a target="_blank" href="https://www.mongodb.com/">Mongo Atlas</a>, an online cloud database, whereas in PostgreSQL I had to set up the database in the production server. Previously, I used Heroku to get the PostgreSQL database so they created and managed it for me. But if I had to use a VPS instead of a service helper, I would need to set up the database myself, which would take more time than I would want to spend for the software. </p>
<p>Additionally, I was not sure of what information I would like to store initially, meaning if I had to add more data, I would need to migrate and update table schemas, which would result in more time fighting a database than working on the application. Thus, I chose the easier option that also allows storing dynamic data without migrations: MongoDB.</p>
<p>However, I had only been used to Mongoose for my schemas, and realized I would have to relearn Mongoose for my application's storage purposes. That's when I found Prisma!</p>
<h3 id="heading-learning-prisma">Learning Prisma</h3>
<p>Prisma allows you to use many databases such as PostgreSQL and MongoDB, and it would manage database migrations, and changing schemas for you. Additionally, it provided a very intuitive API for creating schemas, querying data, creating records, and more. After another day of research and testing, MongoDB still seemed like the simpler database to use, so I chose to use MongoDB with Prisma.</p>
<blockquote>
<p>There were some issues using MongoDB locally, since Primsa required a replica set database and I had a standalone, so I used a MongoDB Atlas database instead, and could continue to focus my remaining time on developing the application.</p>
</blockquote>
<h2 id="heading-how-it-generates-endpoints">How it generates endpoints</h2>
<p>A major design decision for 10ME was whether user accounts were necessary or not. Maybe with user accounts, endpoints could last longer than 10 minutes.</p>
<h3 id="heading-scrapping-the-idea-of-user-accounts">Scrapping the idea of user accounts</h3>
<p>However, a user system would require adding authentication, registering, validation, and more which one usually spends a significant amount of time on.</p>
<p>My previous knowledge used sessions logging in (with <a target="_blank" href="http://www.passportjs.org/packages/passport-local/">passport local</a>), and maybe existing authentication forms may be faster (such as <a target="_blank" href="https://www.oauth.com/">OAuth2</a>), but I did not want to add more data to be stored in the database. </p>
<p>Additionally, signing up acts as a barrier to using a service; I just wanted users to visit the page, get their logs, and leave the page when they're done.</p>
<h3 id="heading-tracking-anonymous-users">Tracking anonymous users</h3>
<p>However, I also needed to keep track of anonymous users, so 10ME won't regenerate the endpoint every time the user refreshes the page. For that, I used <a target="_blank" href="https://www.npmjs.com/package/express-session">express session</a> to keep track of user sessions that visit the service.</p>
<p>When users visit for the first time, a user code is created and assigned to them, and stays with them until it expires. When it expires, it is deleted from their session, so when they visit the page again, a new code is generated and assigned to them.</p>
<h2 id="heading-how-it-deletes-endpoints">How it deletes endpoints</h2>
<p>Every time the server is rebooted, all previous sessions are reset, so even if a user's 10 minutes are not complete, a new session would be created for them if they refresh the page.</p>
<blockquote>
<p>This could possibly be prevented by using <a target="_blank" href="https://expressjs.com/en/resources/middleware/cookie-session.html">cookie sessions</a>, but I have to explore that option.</p>
</blockquote>
<p>That means it is possible an endpoint can expire, but not be deleted right away. Rather than set a timer to delete each endpoint as they are created, the server has one global timer that deletes all expired endpoints every 10 minutes.</p>
<h2 id="heading-how-it-logs-requests">How it logs requests</h2>
<p>Similar to how 10 Minute Mail provides a temporary email, 10ME uses the code generated for the user as their endpoint, accepting requests at the <code>"https://10minuteendpoint.net/endpoint/&lt;code&gt;"</code> url. At the <code>"/endpoint"</code> route, requests received are stored with the associated endpoint code, with the request's method, body and query data.</p>
<h2 id="heading-how-endpoint-logs-are-displayed">How endpoint logs are displayed</h2>
<p>Ideally, when a request is logged, I wanted it to instantly be displayed on the developer dashboard.</p>
<p>One way to do that would be to have an existing websocket connection between the server and developer (client) so the data can be sent to client right away, or server-sent events (SSE) can be pushed from the server to the client on data changes.</p>
<p>However, setting up websockets or SSE turned out time consuming, especially when considering deployment, and having to configure ports and nginx. Thus, the client browser simply polls the server API for new logs. A fetch request to the <code>"https://10minuteendpoint.net/api/logs/&lt;code&gt;"</code> url is sent every second, and all associated logs are returned to the client.</p>
<h3 id="heading-disadvantage-of-polling-server">Disadvantage of polling server</h3>
<p>After polling the server for logs, it sends all existing logs to the client which then creates the associated html to display it to the developer. Sending all the log data on each request can become resource intensive for the server, potentially causing performance issues.</p>
<h2 id="heading-slice-of-life-optimizations">Slice of Life Optimizations</h2>
<p>There are many things I was unable to add due to time, which I would add if I'm able to continue working on 10ME:</p>
<h3 id="heading-lowering-potential-performance-issues">Lowering Potential Performance Issues</h3>
<p>To minimise the potential performance issues, I can limit each endpoint to store a maximum of 25 logs at once, with new logs replacing older logs in a first in first out flow.</p>
<h3 id="heading-spam-protection-dos-prevention">Spam Protection (DOS Prevention)</h3>
<p>It is possible that malicious users can send requests requests their endpoints on loop (or even try logging random endpoints). To prevent DOS (Denial of Service) attacks, I can use <a target="_blank" href="https://github.com/abriginets/umbress">Umpress</a> in NodeJS on all routes.</p>
<blockquote>
<p>Fortunately, Linode <a target="_blank" href="https://www.linode.com/products/ddos/">automatically detects and mitigates</a> such attacks from happening.</p>
</blockquote>
<h3 id="heading-extendingrenewing-endpoints">Extending/Renewing Endpoints</h3>
<p>10 Minute Mail has the option to extend email addresses to 10 minutes, or renew expired email addresses. To add these features to 10ME, I can create an API that increases the expiry time of the endpoint, but that would only happen if the endpoint is not deleted by the 10 minute loop the server uses.</p>
<p>Currently, once the 10 minutes have passed, the user no longer has access to their logs or the endpoints. Seeing as this is just for testing purposes for the developer, I don't see it as something they can lose data with.</p>
<h2 id="heading-honest-review">Honest review</h2>
<p>In terms of service usage, 10ME successfully solves the original problem I had of testing webhooks and endpoints <em>(although similar services already exist with more features)</em>.</p>
<p>In terms of open source software, 10ME is robust and somewhat hard coded, with just enough code to achieve the simple scope of the project. I did follow as much best practices I am aware of, with plans to make the software more flexible in the future. </p>
<p>I also added a <a target="_blank" href="https://github.com/danidre14/10-minute-endpoint-nodejs#readme">readme</a> so other developers can deploy their own instances of 10 Minute Endpoint, and for now it is up to them to extend the functionality for their specific needs.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>At the end of development, I learnt of many tools (closed source or bundled with other features) that provided similar solutions like 10ME, such as <a target="_blank" href="https://webhook.site">Webhook Site</a>, <a target="_blank" href="https://pipedream.com">Pipedream</a>, <a target="_blank" href="https://requestcatcher.com">Request Catcher</a>, <a target="_blank" href="https://endpoints.dev">Endpoints</a>, and <a target="_blank" href="https://ptsv2.com">PTSV2</a>. However, there is still room for the likes of 10 Minute Endpoint.</p>
<p>10 Minute Endpoint logs requests as minimally as possible, does not store user data, deletes endpoint data as quickly as possible, and aims to be used as easily as possible, prioritising simplicity and smooth intuitive usage flow.</p>
<p>Despite the spaghetti code included due to the limited scope and time available, I did learn many new things developing this project, such as database management with Prisma, webhooks, deploying to custom virtual private servers, and how to use the <a target="_blank" href="https://bulma.io/">Bulma</a> framework to style your applications.</p>
<p>You can check out the application <a target="_blank" href="https://10minuteendpoint.net">here</a> and the source code <a target="_blank" href="https://github.com/danidre14/10-minute-endpoint-nodejs">here</a>. I hope 10ME can be useful to you, and I look forward to all your open source projects as well! Thanks for reading.</p>
<hr />

<p>If you appreciate my work, consider following me on <a target="_blank" href="https://twitter.com/danidre">Twitter</a> and supporting me on <a target="_blank" href="https://paypal.me/danidre">PayPal</a> or <a target="_blank" href="https://www.patreon.com/danidre">Patreon</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Introducing 10 Minute Endpoint: Webhook Testing Made Easy]]></title><description><![CDATA[Introduction
Have you ever needed to integrate software to a server, where you do not have access to the server logs, and no control over what the external API sends to the server? What if you are learning to make HTTP requests and need to confirm th...]]></description><link>https://blog.danidre.com/introducing-10-minute-endpoint</link><guid isPermaLink="true">https://blog.danidre.com/introducing-10-minute-endpoint</guid><category><![CDATA[Linode]]></category><category><![CDATA[Linode Hackathon]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Thu, 30 Jun 2022 03:28:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1656352740377/nM2eBH-SE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Have you ever needed to integrate software to a server, where you do not have access to the server logs, and no control over what the external API sends to the server? What if you are learning to make HTTP requests and need to confirm that you send the correct data? In both these cases, it could be convenient to be able to inspect the data being sent. That's where 10 Minute Endpoint can help.</p>
<p><a target="_blank" href="https://10minuteendpoint.net/"><strong>10 Minute Endpoint</strong></a> is an open-source service that generates a temporary endpoint for a developer to test and inspect HTTP requests sent to it. This way, they can easily practice making HTTP (or fetch) requests by sending them to this url, or they can use this url in a third party service to easily see the data it sends to their endpoint, which can be useful when developing their APIs that may need certain fields of data.</p>
<h1 id="heading-why-10-minute-endpointhttps10minuteendpointnet">Why <a target="_blank" href="https://10minuteendpoint.net/">10 Minute Endpoint</a>?</h1>
<p>Existing services store an exhausting amount of information per request, and the records are publicly available. If a third party gets access to the endpoint link, they can view all request logs, which can contain private information of that developer. Additionally, these services allow options to register and log in, storing more data about its users.</p>
<h2 id="heading-benefits-for-developers">Benefits for developers</h2>
<p>10 Minute Endpoint addresses all these concerns by:</p>
<ul>
<li>Recording as little data as necessary. Only a request's method name, query data, and body data.</li>
<li>Allowing anonymous users. It does not need to store user information.</li>
<li>Making all logs private by default. Only the user that received the endpoint link can view the logs sent to that link.</li>
<li>Deleting data as quickly as possible. The endpoints as well as the associated logs are all deleted after 10 minutes.</li>
</ul>
<h1 id="heading-features-roadmap">Features roadmap</h1>
<p>10 Minute Endpoint prioritizes simplicity and minimalism. For that, the feature list is minimal, with enhancements possibly planned for the future:</p>
<ul>
<li><p>[x] <strong>v 0.1</strong></p>
<ul>
<li>[x] Set up NodeJS application</li>
<li>[x] Add readme and license</li>
<li>[x] Create test server</li>
<li>[x] Set up managed routes</li>
<li>[x] Set up github repo</li>
</ul>
</li>
<li><p>[x] <strong>v 0.2</strong></p>
<ul>
<li>[x] Track anonymous users by session</li>
<li>[x] Generate endpoint for random user</li>
<li>[x] Let endpoint expire after 10 minutes</li>
<li>[x] Show expiry countdown on client</li>
</ul>
</li>
<li><p>[x] <strong>v 0.5</strong></p>
<ul>
<li>[x] Set up database</li>
<li>[x] Start recording endpoint requests</li>
<li>[x] Store endpoint data in database</li>
<li>[x] Display endpoint data to client</li>
<li>[x] Delete expired data from database</li>
</ul>
</li>
<li><p>[x] <strong>v 0.7</strong></p>
<ul>
<li>[x] Style frontend client</li>
<li>[x] Add favicon and meta tags</li>
<li>[x] Add 404 page for all other requests</li>
<li>[x] Block bots and web crawlers from using service</li>
</ul>
</li>
<li><p>[x] <strong>v 1.0</strong></p>
<ul>
<li>[x] Deploy to Linode</li>
<li>[x] Configure nginx proxies for public use</li>
<li>[x] Enable cors to allow requests to endpoints form anywhere</li>
</ul>
</li>
</ul>
<ul>
<li>[ ] <strong>v 1.1</strong><ul>
<li>[ ] Limit requests logged per endpoint</li>
<li>[ ] Allow extending expiry date</li>
<li>[ ] Allow renewing expired endpoint</li>
<li>[ ] Allow filtering logs by method</li>
<li>[ ] Implement custom DDoS protection (currently handled by Linode)</li>
<li>[ ] Allow user to send default json responses from requests</li>
<li>[ ] Allow user to view request source (IP)</li>
</ul>
</li>
</ul>
<h1 id="heading-built-with">Built With</h1>
<p>10ME was developed with the PLEB stack:</p>
<ul>
<li>Database/storage with <strong>Prisma</strong> (MongoDB)</li>
<li>DevOps/deployed on <strong>Linode</strong></li>
<li>Backend/API with <strong>Express</strong> (NodeJS)</li>
<li>Frontend/design using <strong>BulmaCSS</strong></li>
</ul>
<h1 id="heading-prerequisites">Prerequisites</h1>
<h3 id="heading-mongodb">Mongodb</h3>
<p>Either install MongoDB locally to your environment, or use one online (through Mongo Atlas for example)</p>
<h3 id="heading-prisma">Prisma</h3>
<p>Prisma requires a replica set instead of standalone database.</p>
<p><strong>Note</strong> - Run <code>prisma generate</code> after installing the packages to generate the MongoDB schemas internally.</p>
<h1 id="heading-running-and-deployment">Running and Deployment</h1>
<ol>
<li><p>Clone the repo:</p>
<pre><code class="lang-sh">git <span class="hljs-built_in">clone</span> https://github.com/danidre14/10-minute-endpoint-nodejs.git
</code></pre>
</li>
<li><p>Set environment variables for the following (using the <code>.env</code> file, for example):</p>
<ul>
<li>NODE_ENV</li>
<li>PORT</li>
<li>SESSION_SECRET</li>
<li>DATABASE_URL
Rename the <code>.env.sample</code> file to <code>.env</code> and use actual credentials obtained in the <a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a>.</li>
</ul>
</li>
<li><p>Install all dependencies:</p>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> /path/to/project
npm install
<span class="hljs-comment"># or yarn install, or other depending on your package manager</span>
</code></pre>
</li>
<li><p>Generate dev dependencies:</p>
<pre><code class="lang-sh">prisma generate
</code></pre>
</li>
<li><p>Run the project:</p>
<pre><code class="lang-sh">npm start
</code></pre>
<p>Open a browser and hit <code>http://localhost:5000</code></p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>10 Minute Endpoint logs requests as minimally as possible, does not store user data, deletes endpoint data as quickly as possible, and aims to be used as easily as possible, prioritizing simplicity and smooth intuitive usage flow.</p>
<p>See the links below to support me or check out the project yourself:</p>
<ul>
<li>Github Link: https://github.com/danidre14/10-minute-endpoint-nodejs</li>
<li>Application Link: https://10minuteendpoint.net</li>
<li>Twitter: <a target="_blank" href="https://twitter.com/danidre">@danidre</a></li>
<li>Patreon: <a target="_blank" href="https://www.patreon.com/danidre">Danidre</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Making of FlevaR]]></title><description><![CDATA[Contents

Introduction
Backstory
Contents
Game Screen
Loading Screen
Mouse Inputs
Keyboard Inputs
Main Loop
Audio Manager
Texture Manager
Scripting Flow
Render Engine
Data Storage with SharedObjects
FlevaClips
Scenes
Virtual Camera
Event Handling
Roo...]]></description><link>https://blog.danidre.com/the-making-of-flevar</link><guid isPermaLink="true">https://blog.danidre.com/the-making-of-flevar</guid><category><![CDATA[THW Web Apps]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[HTML Canvas]]></category><category><![CDATA[Browsers]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Sat, 21 May 2022 01:14:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1662513142493/NC3lcNniY.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-contents">Contents</h1>
<ul>
<li><a class="post-section-overview" href="#heading-introduction">Introduction</a></li>
<li><a class="post-section-overview" href="#heading-backstory">Backstory</a></li>
<li><a class="post-section-overview" href="#heading-contents">Contents</a></li>
<li><a class="post-section-overview" href="#heading-game-screen">Game Screen</a></li>
<li><a class="post-section-overview" href="#heading-loading-screen">Loading Screen</a></li>
<li><a class="post-section-overview" href="#heading-mouse-inputs">Mouse Inputs</a></li>
<li><a class="post-section-overview" href="#heading-keyboard-inputs">Keyboard Inputs</a></li>
<li><a class="post-section-overview" href="#heading-main-loop">Main Loop</a></li>
<li><a class="post-section-overview" href="#heading-audio-manager">Audio Manager</a></li>
<li><a class="post-section-overview" href="#heading-texture-manager">Texture Manager</a></li>
<li><a class="post-section-overview" href="#heading-scripting-flow">Scripting Flow</a></li>
<li><a class="post-section-overview" href="#heading-render-engine">Render Engine</a></li>
<li><a class="post-section-overview" href="#heading-data-storage">Data Storage with SharedObjects</a></li>
<li><a class="post-section-overview" href="#heading-flevaclips">FlevaClips</a></li>
<li><a class="post-section-overview" href="#heading-scenes">Scenes</a></li>
<li><a class="post-section-overview" href="#heading-virtual-camera">Virtual Camera</a></li>
<li><a class="post-section-overview" href="#heading-event-handling">Event Handling</a></li>
<li><a class="post-section-overview" href="#heading-root-proxy">Root Proxy</a></li>
<li><a class="post-section-overview" href="#heading-performance-disclaimer">Performance Disclaimer</a></li>
<li><a class="post-section-overview" href="#heading-flevars-future">Flevar's Future</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h1 id="heading-introduction">Introduction</h1>
<p>This is a long overdue article, and its creation was encouraged by <a target="_blank" href="https://townhall.hashnode.com/the-epic-hashnode-writeathon">The Epic Hashnode Writeathon</a>! It is targeted at the Web Apps category, about building browser-based games, and will be an documentation of how I —Danidre, creator of FlevaR— developed <a target="_blank" href="https://flevar.com/">FlevaR</a>, a declarative JavaScript game engine for creating 2D browser games and applications.</p>
<p>This article does not aim to be a chronological sequence of the engine's progress, nor does it aim to throw all the engine's code at you. Rather, it aims to be an overview of the current state of the game engine, highlighting the reasons behind the implementations of each component, what inspired me to make each decision, and what I would do differently in the future. I hope you enjoy!</p>
<h1 id="heading-backstory">Backstory</h1>
<p>At the end of 2020, browsers were ending support for Flash games, meaning the plugins would no longer work. Game developers needed to find other ways to develop and distribute their games. Since its official announcement in 2017, I began learning JavaScript on <a target="_blank" href="https://www.sololearn.com/home">SoloLearn</a>, and had many prototypes of games and game creators. For some reason, I could only find PhaserJS and PixiJS as alternative browser engines at the time, and the idea of learning something new was daunting, so I decided to create it myself.</p>
<p>One random day in 2020, (and coincidentally a week before Ludum Dare 45 and Geta Game Jam 11), an idea suddenly struck me: What if someone can say "an object exists at this position, and looks this way", and the internal code would handle making it look that way, and place it in the intended destination? I got to coding right away, and a version ready in time for testing out through the game jams. Since then, I've continued to fix bugs, add features, and develop the game engine into its current state (version 2.2.0).</p>
<p>Right now, the engine's main features include textures, audio, collision detection, keyboard and mouse inputs, saving/loading, a virtual camera, reusable scripts, a pre-loader, and its own entity object, called a flevaclip. It was heavily influenced by state hooks <em>inspired by ReactJS</em>, callbacks <em>inspired by ExpressJS and KnexJS</em>, and APIs and abstractions <em>inspired by Flash</em>. Most features are created by abstracting away the complex events and functions offered by modern browsers, and providing them with simple APIs that the developer can use to create whatever game or application they wish to make.</p>
<h3 id="heading-flevars-motto">FlevaR's Motto</h3>
<p>FlevaR does not focus on being an engine for a specific genre of games, limiting the possibilities of developers. FlevaR simply <strong>provides</strong> the APIs as much as possible abstracted from the browser. The developer is the one that <strong>decides</strong> which ones to use for their creations, limited only by their imaginations.</p>
<h1 id="heading-game-screen">Game Screen</h1>
<p>The first thing a player sees on the game's page, is the screen where everything is rendered.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Internally, the game screen consists of a <code>&lt;canvas&gt;</code> HTML5 element wrapped within a <code>&lt;div&gt;</code> HTML5 element (hence called <code>container</code>). Originally, only a <code>&lt;canvas&gt;</code> element was used. However, this proved difficult when I had to consider fullscreen or resizable applications. Wrapping the <code>&lt;canvas&gt;</code> inside a <code>container</code> that the engine can control would allow direct control of the position of the canvas based on absolute and relative positions in the browser:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvasdiv = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
canvasdiv.setAttribute(<span class="hljs-string">"style"</span>, <span class="hljs-string">`border-style: solid; border-width: 2px; width: <span class="hljs-subst">${_width}</span>px; height: <span class="hljs-subst">${_height}</span>px; position: relative;`</span>);
canvasdiv.style.overflow = <span class="hljs-string">"hidden"</span>;
canvasdiv.style.outline = <span class="hljs-string">"none"</span>;
canvasdiv.style.border = <span class="hljs-string">"none"</span>;
canvasdiv.tabIndex = <span class="hljs-string">"0"</span>;

<span class="hljs-keyword">const</span> canvas = helperFunction.createCanvas(_width, _height);
canvas.setAttribute(<span class="hljs-string">"style"</span>, <span class="hljs-string">"position: absolute;"</span>);
<span class="hljs-keyword">const</span> globalCtx = canvas.getContext(<span class="hljs-string">"2d"</span>);
globalCtx.imageSmoothingEnabled = <span class="hljs-literal">false</span>;

canvasdiv.appendChild(canvas);
browserdiv.appendChild(canvasdiv);
</code></pre>
<p>It also allowed for internal calculations to handle the scaling and position of the canvas (such that it can properly be centered in screen sizes of different aspect ratios).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651189224996/G4N04MAQa.JPG" alt="screen_with_black_borders.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>When the developer initializes the engine, they must provide a default element that the engine will use, as well as options such as the dimensions of the screen, or fps of the application:</p>
<h3 id="heading-indexhtml">index.html</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- container for our application --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"flevaRContainer"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<h3 id="heading-mainjs">main.js</h3>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> flevaRContainerElement = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"flevaRContainer"</span>);
FlevaR(flevaRContainerElement, { <span class="hljs-attr">_width</span>: <span class="hljs-number">600</span>, <span class="hljs-attr">_height</span>: <span class="hljs-number">500</span>, <span class="hljs-attr">fps</span>: <span class="hljs-number">30</span> });
</code></pre>
<p>Then, a screen of appropriate dimensions will be created and used to render all scenes and flevaclips.
The resulting HTML would look something like the following:</p>
<h3 id="heading-indexhtml">index.html</h3>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- container for our application --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"flevaRContainer"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"border: none; width: 600px; height: 500px; position: relative; overflow: hidden; outline: none; cursor: default;"</span> <span class="hljs-attr">tabindex</span>=<span class="hljs-string">"0"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"600"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"position: absolute; left: 0px; top: 0px;"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<blockquote>
<p>The <code>tabindex</code> allowed the canvas to receive focus for mouse and key inputs.
The <code>left</code> and <code>top</code> are adjusted internally to position the canvas in the middle of the screen.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651189274956/qHs0vGQ8h.JPG" alt="screen_with_black_borders_2.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-screen-occupancy">Screen occupancy</h3>
<p>Currently, the canvas only stretches to fit the provided dimensions, and multiple aspect ratios are not supported. In the future I plan on modifying the canvas (and <a class="post-section-overview" href="#heading-render-engine">render engine</a>) to resize to fill the spaces shown by the black borders. This can be useful especially with mobile devices and landscape/portrait orientations.</p>
<h3 id="heading-multiple-wrapped-html-elements">Multiple wrapped HTML elements</h3>
<p>Since the canvas is already wrapped in a div, if I needed to add extra elements (such as error logs when an application crashes), I could easily include it in the div without having to worry about whether the developer's website's HTML will be structured in a format to accept it.</p>
<h3 id="heading-multiple-canvases">Multiple canvases</h3>
<p>I plan on extending the engine to allow developing mobile/touchscreen games. A second canvas can be useful for displaying virtual joysticks that can be configured by the developer, and used easily by the player.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651191502567/1ZhQGfZia.png" alt="virtual_joysticks_sample.png" class="image--center mx-auto" /></p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-loading-screen">Loading Screen</h1>
<p>Depending on the configurations of the game, the built-in loader is shown, and all the assets (textures and audio) is loaded in the browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651262379790/UA26Gi4yo.JPG" alt="loading_screen_progress.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Internally, all assets passed to the engine are initially queued, and one by one loaded as JavaScript <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>, which are resolved when the onload event for the image (or onloadeddata for audio) is called. The length of the loading bar is calculated then by the current number of assets loaded divided by the initial length of the queue.</p>
<pre><code class="lang-js"><span class="hljs-comment">// ...other loading code</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> asset <span class="hljs-keyword">of</span> queueList) {
    <span class="hljs-keyword">const</span> source = asset.source;
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> source.loadSource();
    } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error while loading assets: "</span> + e);
    }
    loadCounter++;
    renderLoadScreen(); <span class="hljs-comment">// updates load bar</span>
}
</code></pre>
<p>When each asset is loaded, the current number increases and the loading bar lengthens. When all assets have been loaded, an option opens to allow the player to click anywhere or press a button to start the game.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651264247074/SDZlH2cES.JPG" alt="loading_screen_complete.JPG" class="image--center mx-auto" /></p>
<p>Internally, I add temporary <code>mousedown</code> and <code>keydown</code> event listeners that start the main loop of the engine and immediately remove the temporary event listeners:</p>
<pre><code class="lang-js"><span class="hljs-comment">// ...other loading code</span>
<span class="hljs-keyword">const</span> finishLoad = <span class="hljs-function">() =&gt;</span> {
    queueList.length = <span class="hljs-number">0</span>;
    <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">"resize"</span>, renderLoadScreen, <span class="hljs-literal">false</span>);
    _screen.div.removeEventListener(<span class="hljs-string">"mousedown"</span>, finishLoad, <span class="hljs-literal">true</span>);
    _screen.div.removeEventListener(<span class="hljs-string">"keydown"</span>, finishLoad, <span class="hljs-literal">true</span>);
}
_screen.div.addEventListener(<span class="hljs-string">"mousedown"</span>, finishLoad, <span class="hljs-literal">true</span>);
_screen.div.addEventListener(<span class="hljs-string">"keydown"</span>, finishLoad, <span class="hljs-literal">true</span>);
</code></pre>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>Within the main load function of the FlevaR application, part of the API provided allows for developers to load their textures and sounds, using relative paths of the assets:</p>
<h3 id="heading-sample-directory">sample directory</h3>
<pre><code><span class="hljs-operator">-</span><span class="hljs-operator">-</span> assets (folder)
   <span class="hljs-operator">-</span><span class="hljs-operator">-</span> nice<span class="hljs-operator">-</span>image.png
   <span class="hljs-operator">-</span><span class="hljs-operator">-</span> cool<span class="hljs-operator">-</span>sound.mp3
<span class="hljs-operator">-</span><span class="hljs-operator">-</span> index.html
<span class="hljs-operator">-</span><span class="hljs-operator">-</span> flevar.js
<span class="hljs-operator">-</span><span class="hljs-operator">-</span> main.js
</code></pre><h3 id="heading-mainjs">main.js</h3>
<pre><code class="lang-js"><span class="hljs-comment">// ...other code</span>
FlevaR(flevaRContainerElement, { <span class="hljs-attr">_width</span>: <span class="hljs-number">600</span>, <span class="hljs-attr">_height</span>: <span class="hljs-number">500</span>, <span class="hljs-attr">fps</span>: <span class="hljs-number">30</span> }, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload</span>(<span class="hljs-params">core</span>) </span>{
    <span class="hljs-comment">/* Within engine's onload */</span>
    <span class="hljs-keyword">const</span> { createSound, createGraphic } = core;
    createSound(<span class="hljs-string">"mySound"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"cool-sound"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"mp3"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"assets"</span> });
    createGraphic(<span class="hljs-string">"myGraphic"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"nice-image"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"png"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"assets"</span> });
});
</code></pre>
<p>These calls send the assets to the queue which is handled internally as described above.</p>
<blockquote>
<p>If the engine had already been loaded by the time the APIs were called, the assets are loaded in the background. Resources that depend on the assets would default to their placeholder assets until they are loaded.</p>
</blockquote>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-parallel-loading">Parallel Loading</h3>
<p>Currently, assets are loaded sequentially, and each asset waits until the other is complete first before loading the next:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> queueList = [asset1, asset2, asset3];
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> asset <span class="hljs-keyword">of</span> queueList) {
    <span class="hljs-comment">// await waits for each promise to be resolved, which occurs when each asset is loaded</span>
    <span class="hljs-keyword">await</span> asset.source.loadSource();
}
<span class="hljs-comment">/*
Sample estimated duration:
========O                   asset1
===========O                asset2
=====O                      asset3
========================O   total
*/</span>
</code></pre>
<p>I recently learnt more capabilities of Promises; you can fire multiple promises and await them all. Rather than one after the other, all assets would begin to load and resolve at shorter times depending on size:</p>
<pre><code class="lang-js"><span class="hljs-comment">// ...pseudocode</span>
<span class="hljs-keyword">const</span> queueList = [asset1, asset2, asset3];
<span class="hljs-keyword">const</span> promiseList = [];
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> asset <span class="hljs-keyword">of</span> queueList) {
    promiseList.push(asset.source.loadSource());
}
<span class="hljs-comment">// wait for the Promise.all to be resolved</span>
<span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(promiseList);
<span class="hljs-comment">/*
Sample estimated duration:
========O      asset1
===========O   asset2
=====O         asset3
===========O   total
*/</span>
</code></pre>
<h3 id="heading-priority-smart-loading">Priority (Smart) Loading</h3>
<p>Right now, all assets are tried and loaded. However, not all assets are needed right away (for example, boss music would not need to be loaded as the game loads). A hope is for the engine to determine which assets need immediate loading and which assets can load in the background in whatever free time is available. This can lower the amount of assets loading at engine startup, and reduce loading duration.</p>
<blockquote>
<p>I have no knowledge on any methods possible to implement this just yet.</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-mouse-inputs">Mouse Inputs</h1>
<p>Right after the engine loads, temporary event listeners allow the player to play the game (start the engine). Similar to <a class="post-section-overview" href="#heading-keyboard-inputs">Keyboard Inputs</a>, a <code>Mouse</code> module is provided by the core of the engine with APIs for developers to handle mouse movement and clicks from the player.</p>
<h3 id="heading-some-supported-apis">Some Supported APIs</h3>
<ul>
<li>Mouse.LEFT</li>
<li>Mouse.MIDDLE</li>
<li>Mouse.RIGHT</li>
<li>Mouse.isDown()</li>
<li>Mouse.isUp()</li>
<li>Mouse.isPressed()</li>
<li>Mouse.isReleased()</li>
</ul>
<p>The <code>x</code> and <code>y</code> coordinates of the mouse pointer is also provided by the engine, although through a different object: <code>_root</code>. The <code>x</code> position is obtained from <code>_root._xmouse</code>, and the <code>y</code> position is obtained from <code>_root._ymouse</code>.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The mouse APIs were heavily influenced by the <strong>ActionScript 2.0</strong> APIs provided by Adobe Flash, although due to the architecture of the <a class="post-section-overview" href="#heading-main-loop">main loop</a>, I added additional functions such as <code>isPressed</code> and <code>isReleased</code>.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Conveniently, all major browsers provide <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent">mouse event listeners</a> that can be used to get the position of the mouse relative to an HTML5 element, as well as determine when mouse actions <em>—such as clicks and scrolls—</em> are fired. </p>
<h3 id="heading-mouse-positions">Mouse Positions</h3>
<p>The convention for many games have coordinates relative to the screen with the top left of the canvas being position <code>[0, 0]</code>, and the bottom right of the canvas being positions equal to the <code>width</code> and <code>height</code> of the canvas. Rather than leave the developer to figure out the mouse positions each frame, FlevaR abstracts this layer away and provides a simpler API for the developer to use. This helps keep the mouse coordinates consistent regardless of how small or large the screen is after initialization.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651270551311/AzJoM3edM.gif" alt="consistent_mouse_coordinates.gif" class="image--center mx-auto" /></p>
<p>The engine adds a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousemove_event"><code>"mousemove"</code></a> event listener to the <code>container</code> on engine load, and from there handles the event, computes the cursor positions based on the location of the cursor in the browser, relative to the top left corner of the <code>container</code>. That value is then offset by the relative position of the canvas wrapped inside the <code>container</code>, and scaled depending on the aspect ratio of the screen, and stored as <code>x</code> and <code>y</code> positions. </p>
<pre><code class="lang-js">_screen.div.addEventListener(<span class="hljs-string">"mousemove"</span>, setMousePosition, <span class="hljs-literal">false</span>);
<span class="hljs-comment">// ...other code</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMousePosition</span>(<span class="hljs-params">_event</span>) </span>{
    <span class="hljs-keyword">const</span> canvas = _screen.canvas;
    <span class="hljs-keyword">const</span> rect = canvas.getBoundingClientRect(),
        scaleX = canvas.width / rect.width,
        scaleY = canvas.height / rect.height;

    <span class="hljs-keyword">const</span> mouseX = <span class="hljs-built_in">Math</span>.ceil((_event.clientX - rect.left) * scaleX / _screen.xScale);
    <span class="hljs-keyword">const</span> mouseY = <span class="hljs-built_in">Math</span>.ceil((_event.clientY - rect.top) * scaleY / _screen.yScale);

    Mouse._xmouse = numberUtils.clamp(mouseX, <span class="hljs-number">0</span>, stage._width);
    Mouse._ymouse = numberUtils.clamp(mouseY, <span class="hljs-number">0</span>, stage._height);
}
</code></pre>
<blockquote>
<p>Other indirect events such as <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mousedown_event"><code>"mousedown"</code></a> (when the mouse pointer is pressed) or <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenchange_event"><code>"fullscreenchange"</code></a> (when the application enters or exits fullscreen) also call the <code>setMousePosition</code> function to keep the internal stored mouse positions up-to-date with changes in the application.</p>
</blockquote>
<p>The <code>Mouse._xmouse</code> and <code>Mouse._ymouse</code> properties are later exposed from the internals of the engine to the developer through the <a class="post-section-overview" href="#heading-root-proxy">root proxy</a>.</p>
<h3 id="heading-mouse-functions">Mouse Functions</h3>
<p>The engine also adds <code>"mousedown"</code>, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseup_event"><code>"mouseup"</code></a>, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event"><code>"mouseleave"</code></a> events to capture those actions and abstract it into easy APIs the developer can use.</p>
<pre><code class="lang-js">_screen.div.addEventListener(<span class="hljs-string">"mousedown"</span>, setMouseDown, <span class="hljs-literal">false</span>);
_screen.div.addEventListener(<span class="hljs-string">"mouseup"</span>, setMouseUp, <span class="hljs-literal">false</span>);
_screen.div.addEventListener(<span class="hljs-string">"mouseleave"</span>, setMouseLeave, <span class="hljs-literal">false</span>);
</code></pre>
<p>Providing <code>isDown</code> and <code>isUp</code> functions was easy: simply store the mouse button being held down, and the function will return a boolean if that button was stored for <code>isDown</code>, and the opposite for <code>isUp</code>.</p>
<p>However, it is not so simple for the <code>isPressed</code> and <code>isReleased</code> functions. Rather than try to explain in theory, here is a simplified process of how each action is handled:</p>
<pre><code class="lang-js"><span class="hljs-comment">// only checking left mouse button for simplicity</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMouseDown</span>(<span class="hljs-params">_event</span>) </span>{
    setMousePosition(_event);
    <span class="hljs-comment">// left mouse button is code 0</span>
    <span class="hljs-keyword">if</span> (_event.button === <span class="hljs-number">0</span>) {
        <span class="hljs-comment">// if the key in the list does not exist,</span>
        <span class="hljs-comment">// set it to true (helpful for isDown)</span>
        <span class="hljs-keyword">if</span> (Mouse._mouseList._left === <span class="hljs-literal">undefined</span>)
            Mouse._mouseList._left = <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// if the key in the pressed list does not exist,</span>
        <span class="hljs-comment">// trigger the mouse pressed function (helpful for isPressed)</span>
        <span class="hljs-keyword">if</span> (Mouse._mousePressed._left === <span class="hljs-literal">undefined</span>)
            <span class="hljs-built_in">this</span>.setMousePressed(_event);
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMousePressed</span>(<span class="hljs-params">_event</span>) </span>{
    <span class="hljs-keyword">if</span> (_event.button === <span class="hljs-number">0</span>) {
        <span class="hljs-comment">// set pressed to true</span>
        Mouse._mousePressed._left = <span class="hljs-literal">true</span>;
        <span class="hljs-comment">// ...other code</span>
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMouseUp</span>(<span class="hljs-params">_event</span>) </span>{
    <span class="hljs-keyword">if</span> (_event.button === <span class="hljs-number">0</span>) {
        <span class="hljs-comment">// if the key is in the list, remove it from the list (helpful for isUp)</span>
        <span class="hljs-keyword">if</span> (Mouse._left) <span class="hljs-keyword">delete</span> Mouse._left;

        <span class="hljs-comment">// if the key is in the pressed list</span>
        <span class="hljs-keyword">if</span> (Mouse._mousePressed._left !== <span class="hljs-literal">undefined</span>) {
            <span class="hljs-comment">// is key in released list does not exist,</span>
            <span class="hljs-comment">// trigger the mouse released function (helpful for isReleased)</span>
            <span class="hljs-keyword">if</span> (Mouse._mouseReleased._left === <span class="hljs-literal">undefined</span>)
                <span class="hljs-built_in">this</span>.setMouseReleased(_event);

            <span class="hljs-comment">// remove key from pressed list</span>
            <span class="hljs-keyword">delete</span> Mouse._mousePressed._left;
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMouseReleased</span>(<span class="hljs-params">_event</span>) </span>{
    <span class="hljs-keyword">if</span> (_event.button === <span class="hljs-number">0</span>) {
        <span class="hljs-comment">// set released to true</span>
        Mouse._mouseReleased._left = <span class="hljs-literal">true</span>;
        <span class="hljs-comment">// ...other code</span>
    }
}
</code></pre>
<p>Internally, three states are used: <code>mouseList</code>, <code>mousePressed</code>, and <code>mouseReleased</code>, and rather than just checking whether the specific button in each state is <code>true</code> or <code>false</code>, three states are used: <code>true</code>, <code>false</code>, <code>undefined</code>. The <code>mouseList</code> is used to add or remove the button from the list, to easily check whether the <code>isDown</code> function works. The engine will simply check whether that button exists in the state (isDown returns true) or not (isDown returns false). Inversely, <code>isUp</code> will check whether the button exists in the state (isUp is false) or not (isUp is true).</p>
<blockquote>
<p><code>isUp</code> actually calls <code>isDown</code> internally and returns the opposite of that result.</p>
</blockquote>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> isUp = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">_key</span>) </span>{
    <span class="hljs-keyword">return</span> !isDown(_key);
}
</code></pre>
<p>For <code>isPressed</code> to work, it is not enough to check whether the button exists in the <code>mousePressed</code> list. That is because if you hold down the button, you want the <code>isPressed</code> to be true only <strong>once</strong> until the key is first released, then true only <strong>once</strong> again the second time it is pressed.</p>
<p>On every <a class="post-section-overview" href="#heading-the-execution-system">loop</a>*, the engine first captures all mousepresses from the event listeners and abstracts them into their appropriate states, runs the tick methods for all scenes and objects which executes the scripts that check for <code>isPressed</code>, and then "clears" the states accordingly. Internally, this simply changes all buttons in the pressed state to false, rather than delete them.</p>
<blockquote>
<p>*This is a simplified section of what the <a class="post-section-overview" href="#heading-the-execution-system">loop</a> does.</p>
</blockquote>
<p>Therefore, if a button is pressed, it is added to the <code>mousePressed</code> state as <code>true</code> on the first state, and then <code>false</code> on every subsequent tick that the key is being held down, until it is removed from the state when the button is released.</p>
<pre><code class="lang-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> _key <span class="hljs-keyword">of</span> <span class="hljs-built_in">Object</span>.keys(_mousePressed)) {
    <span class="hljs-keyword">if</span> (_mousePressed[_key] === <span class="hljs-literal">true</span>) _mousePressed[_key] = <span class="hljs-literal">false</span>;
}
</code></pre>
<p>The <code>isPressed</code> function only checks if the button is <code>true</code> in the <code>mousePressed</code> state, and as such is only true the first time, each time the button is pressed.</p>
<p>Similarly, <code>isReleased</code> checks if the button exists in the <code>mouseReleased</code> state, which is added once when the button is released, stored in the state, handled once in the tick, and subsequently removed from the list right after when the list is cleared.</p>
<pre><code class="lang-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> _key <span class="hljs-keyword">of</span> <span class="hljs-built_in">Object</span>.keys(_mouseReleased)) {
    <span class="hljs-keyword">delete</span> _mouseReleased[_key];
}
</code></pre>
<h3 id="heading-mouse-codes">Mouse Codes</h3>
<p>The Mouse Module provides pre-computed codes for the <code>"left"</code> (<code>Mouse.LEFT</code>, which is code <code>0</code>), <code>"middle"</code> (<code>Mouse.MIDDLE</code>, which is <code>1</code>), and <code>"right"</code> (<code>Mouse.RIGHT</code>, which is <code>2</code>) mouse buttons.</p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>Within any script, the developer can call the APIs in their scripts to detect when mouse activity occurs, as well as which mouse button triggered the activity:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> x = _root._xmouse;
<span class="hljs-keyword">const</span> y = _root._ymouse;

<span class="hljs-keyword">if</span>(Mouse.isDown(Mouse.RIGHT)) {
    <span class="hljs-built_in">this</span>._text = <span class="hljs-string">"Right button is down"</span>;
} <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">this</span>._text = <span class="hljs-string">"Right button is not down"</span>;
}
<span class="hljs-keyword">if</span>(Mouse.isPressed(<span class="hljs-string">"left"</span>)) {
    <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>._fontColor === <span class="hljs-string">"black"</span>)
        <span class="hljs-built_in">this</span>._fontColor = <span class="hljs-string">"white"</span>;
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">this</span>._fontColor = <span class="hljs-string">"black"</span>;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651353811002/X3K6VzlEF.gif" alt="consistent_mouse_functions.gif" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-cleaner-mouse-api">Cleaner Mouse API</h3>
<p>Right now, mouse positions are obtained using the root proxy, and mouse functions are called using the <code>Mouse</code> module. This was done mainly to copy AS2 APIs provided by Adobe Flash. In hindsight, I see no need for separate objects having mouse-related values. Having the mouse positions simply be provided to the developer as <code>Mouse._x</code> and <code>Mouse._y</code> would be better both for simplicity, as well as speed.</p>
<blockquote>
<p>The root proxy is slow. Check <a class="post-section-overview" href="#heading-root-proxy">that section</a> to find out why.</p>
</blockquote>
<h3 id="heading-touch-support">Touch Support</h3>
<p>Inspired by Adobe Flash's AS2 API, I modelled as much as possible behind it; there was no native touchscreen support, so I had not even considered that in the engine. However, many game engines have support for it, and many people play mobile browser games. In the future, along with <a class="post-section-overview" href="#heading-multiple-canvases">virtual joysticks</a>, I want to add a <code>Touch</code> module with similar APIs to the <code>Mouse</code> and <code>Key</code> modules <em>(with obvious differences to support all 10 touch points)</em>. To achieve this, I plan to use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Touch_events">touch events</a> provided by modern browsers, and abstract accordingly.</p>
<h3 id="heading-general-pointer-support">General Pointer Support</h3>
<p>Recent browsers have general support for <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events">Pointer Events</a>, which include mouse, pen/stylus or touch. Abstracting and providing this API as a <code>Pointer</code> module can continue to help developers use it for anything, such as a drawing application created in FlevaR that uses a stylus. <a class="post-section-overview" href="#heading-flevars-motto"><strong><em>FlevaR provides; Developer decides.</em></strong></a></p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-keyboard-inputs">Keyboard Inputs</h1>
<p>Right after the engine loads, temporary event listeners allow the player to play the game (start the engine). Similar to <a class="post-section-overview" href="#heading-mouse-inputs">Mouse Inputs</a>, a <code>Key</code> module is provided by the core of the engine with APIs for developers to handle keyboard actions from the player.</p>
<h3 id="heading-some-supported-apis">Some Supported APIs</h3>
<ul>
<li>Key.UP</li>
<li>Key.A</li>
<li>Key.ENTER</li>
<li>Key.isDown()</li>
<li>Key.isUp()</li>
<li>Key.isPressed()</li>
<li>Key.isReleased()</li>
</ul>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The keyboard APIs were heavily influenced by the <strong><a target="_blank" href="https://flylib.com/books/en/2.93.1.45/1/">ActionScript 2.0</a></strong> APIs provided by Adobe Flash; although due to the architecture of the <a class="post-section-overview" href="#heading-main-loop">main loop</a>, I added additional functions such as <code>isPressed</code> and <code>isReleased</code>.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Conveniently, all major browsers provide <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent">keyboard event listeners</a> that can be used to determine when keyboard actions are fired. The engine uses <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/keydown_event"><code>"keydown"</code></a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Document/keyup_event"><code>"keyup"</code></a> events to capture these actions and abstract it into easy APIs the developer can use.</p>
<pre><code class="lang-js">_screen.div.addEventListener(<span class="hljs-string">"keydown"</span>, setKeyDown, <span class="hljs-literal">true</span>);
_screen.div.addEventListener(<span class="hljs-string">"keyup"</span>, setKeyUp, <span class="hljs-literal">false</span>);
</code></pre>
<p>There are many ways to determine the specific key fired in the browser event listeners. For example, if the <code>"A"</code> key is pressed, that key can be obtained with one of the following:</p>
<pre><code class="lang-js"><span class="hljs-comment">// assuming the "A" key is pressed</span>
<span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-keyword">let</span> {which, keyCode, code, key} = event;
    <span class="hljs-built_in">console</span>.log([which, keyCode, code, key]);
    <span class="hljs-comment">// outputs [65, 65, "KeyA", "a"]</span>
</code></pre>
<p>In keeping with Flash's AS2 API, I wanted to allow developers to input keyCode numbers (<code>65</code>) or pre-computed properties (<code>Key.LEFT</code>, which is also keyCode <code>35</code>); but I also wanted more flexibility, such as key strings (<code>"a"</code>, or <code>"enter"</code>) and more pre-computed properties not included in Flash AS2 (<code>Key.A</code>, or <code>Key.ONE</code>). To achieve this, both the key codes and key names are stored internally:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setKeyDown</span>(<span class="hljs-params">event</span>) </span>{
    <span class="hljs-keyword">const</span> keyCode = <span class="hljs-string">"code_"</span> + event.keyCode;
    <span class="hljs-keyword">const</span> keyName = <span class="hljs-string">"name_"</span> + event.key.toLowerCase();
    <span class="hljs-keyword">if</span> (Key._keysList[keyCode] === <span class="hljs-literal">undefined</span>) Key._keysList[keyCode] = <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">if</span> (Key._keysList[keyName] === <span class="hljs-literal">undefined</span>) Key._keysList[keyName] = <span class="hljs-literal">true</span>;
    <span class="hljs-comment">// ...other code</span>
</code></pre>
<p>The values prepended by <code>"code_"</code> and <code>"name_"</code> help distinguish between the types. If the "A" key was pressed, it will be stored both as <code>"code_65"</code> and <code>"name_a"</code>. Thus, when checking for an existing key (comparing in the <code>isDown</code> for example), the appropriate state will be checked:</p>
<pre><code class="lang-js">Key.isDown = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">_key</span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> _key === <span class="hljs-string">"string"</span>) {
        _key = _key.toLowerCase();
        <span class="hljs-keyword">if</span> (KeyMap[_key]) _key = KeyMap[_key];
    }

    <span class="hljs-keyword">const</span> keyDown = Key._keysList[<span class="hljs-string">"code_"</span> + _key] ||
        Key._keysList[<span class="hljs-string">"name_"</span> + _key] || <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">return</span> keyDown === <span class="hljs-literal">true</span>;
}
</code></pre>
<blockquote>
<p>Internally, the <code>KeyMap</code> stores all irregular key names and their corresponding key codes (<code>"alt"</code> -&gt; <code>18</code>; <code>"zero"</code> -&gt; <code>48</code>).</p>
</blockquote>
<p>The function accepts the value passed and:</p>
<ol>
<li><p>If it is a string, use the lowercase of that string and map it to the corresponding key code. <br /> <strong><em>Example:</em></strong>  Calling <code>Key.isDown("FIVE")</code> will internally look for the key code 53, etc.</p>
</li>
<li><p>Compare whether that <code>_key</code> value —prepended with <code>"code_"</code> or <code>"name_"</code>— is in the keylist state. <br /> <strong><em>Example:</em></strong> Calling <code>Key.isDown(65)</code> will compare with both <code>"code_65"</code> and <code>"name_65"</code>, which will match with the first option if that key is in the list. <br /> Calling <code>Key.isDown("B")</code> will compare with both <code>"code_b"</code> and <code>"name_b"</code>, which will match with the second option if that key is in the list.</p>
</li>
<li><p>Return the result.</p>
</li>
</ol>
<p>Evidently, options such as <code>"name_65"</code> and <code>"code_b"</code> will never exist, but the double setting allows for the most flexibility.</p>
<blockquote>
<p>In modern browsers, both <code>keyCode</code> and <code>which</code> properties are considered deprecated. However, they were still implemented in keeping with Flash's AS2 API —as well as to, once again, provide key flexibility— with an encouragement to use the modern suggested <code>key</code> property.</p>
</blockquote>
<h3 id="heading-keyboard-functions">Keyboard Functions</h3>
<p>Similar to the Mouse Module, FlevaR's Key <code>isDown</code>, <code>isUp</code>, <code>isPressed</code> and <code>isReleased</code> functions are implemented with the three states and managed with the <a class="post-section-overview" href="#heading-the-execution-system">loop</a> system.</p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>Within any script, the developer can call the APIs in their scripts to detect when keyboard activity occurs, as well as which key triggered the activity:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within flevaclip's onload */</span>
<span class="hljs-keyword">const</span> speed = <span class="hljs-number">5</span>;
<span class="hljs-keyword">if</span>(Key.isDown(Key.A)) {
    <span class="hljs-built_in">this</span>._x -= speed;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(<span class="hljs-string">"d"</span>)) {
    <span class="hljs-built_in">this</span>._x += speed;
}
<span class="hljs-keyword">if</span>(Key.isDown(<span class="hljs-number">87</span>)) { <span class="hljs-comment">// w</span>
    <span class="hljs-built_in">this</span>._y -= speed;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(<span class="hljs-string">"S"</span>)) {
    <span class="hljs-built_in">this</span>._y += speed;
}
<span class="hljs-keyword">if</span>(Key.isDown(Key.Q)) {
    <span class="hljs-built_in">this</span>._rotation -= speed;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(<span class="hljs-number">69</span>)) { <span class="hljs-comment">// e</span>
    <span class="hljs-built_in">this</span>._rotation += speed;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651539707703/CKFJb87E9.gif" alt="key_down_examples.gif" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-gamepad-support">Gamepad Support</h3>
<p>Inspired by Adobe Flash's AS2 API, I modelled as much as possible behind it; I was not aware of any native gamepad support, so I had not even considered that in the engine. However, many game engines have support for it on browsers, and it is also something I personally want to provide in the engine. To achieve this, I plan to use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API">gamepad APIs</a> provided by modern browsers, and abstract accordingly.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-main-loop">Main Loop</h1>
<p>After the engine has been loaded the main loop begins. For FlevaR, this loop is basically a function that is called repeatedly for the duration of the game, which gives the player the illusion of an animation and game. In this loop, all scripts are executed and objects are rendered. However, many more operations occur within each loop, and with the help of events captured by the browser, the general internal execution system of the engine is as follows:</p>
<h3 id="heading-the-execution-system">The Execution System</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651622769588/O6U-vKadW.png" alt="FlevaR Internal Execution System.drawio.png" class="image--center mx-auto" /></p>
<h4 id="heading-check-for-background-assets">Check for Background Assets</h4>
<p>The main assets loaded before the game begins are created within the engine's initialization script. Therefore, it is possible that the developer would have code stored elsewhere to load assets at other points in the running of the game (after it has loaded). The engine simply checks the queue for assets and sends them to be loaded in the background (if they aren't already in the process of being loaded)</p>
<h4 id="heading-run-scripts-tick-method">Run Scripts (Tick Method)</h4>
<p>Scripts are created and attached to objects as callbacks. The tick method crawls through the engine core, current scene, and active flevaclips and executes the scripts the developer would have written. The order of script execution depends on the order the object was added to the screen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651628888333/sVPOf7M1B.gif" alt="Sequential Tick Order.gif" class="image--center mx-auto" /></p>
<h4 id="heading-clear-input-states">Clear Input States</h4>
<p>After the scripts are executed, the input states would be refreshed to be recaptured by <code>Browser Event Listeners</code> and abstracted again. This helps methods such as <code>isPressed</code> and <code>isReleased</code> on the <a class="post-section-overview" href="#heading-mouse-inputs"><code>Mouse</code></a> and <a class="post-section-overview" href="#heading-keyboard-inputs"><code>Key</code></a> modules work.</p>
<h4 id="heading-render-objects-render-method">Render Objects (Render Method)</h4>
<p>Although each object has its own <code>tick</code> and <code>render</code> methods, they are called separately. Unlike the tick method, the order of rendering is not only dependent on the order the object was added to the screen. Flevaclips can have varying depths, and are added to a list based on their depth value. It is very possible that the first flevaclip has a higher depth than the last flevaclip, and the 3rd flevaclip has a lower depth than the 8th flevaclip, for example. When rendering, the list is traversed in ascending order and flevaclip rendered; thus, flevaclips with a higher depth would be rendered <strong>after</strong> a flevaclip with a lower depth, and as such would be seen on top.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651629220624/FlEoco4_E.gif" alt="NonSequential Render Order.gif" class="image--center mx-auto" /></p>
<blockquote>
<p>The depth sequence of the above flevaclips, from lowest to highest, would be: 1, 2, 3 (left); and 1, 3, 2 (right).</p>
</blockquote>
<h4 id="heading-check-for-concurrent-assets">Check for Concurrent Assets</h4>
<p>Rather than waiting for extra assets to be loaded in the background, some developers may want to pause the game until they are loaded. At this stage, any assets added with the concurrent option would then be loaded.</p>
<blockquote>
<p>Some browsers never load assets if the page is not currently selected. Thus, this function is only called if the page is focused.</p>
</blockquote>
<h4 id="heading-resolve-queued-functions">Resolve Queued Functions</h4>
<p>Some engine functions must be executed asychronously, even if the developer does not explicitly do so. Additionally, other functions must be queued until the player interacts with the application (such as playing audio). Depending on the state of the engine at that point of method call, the functions may be executed right away or queued and executed at a later time.</p>
<blockquote>
<p>That "later time" is at the end of the loop cycle, after scripts are ran, assets and states are managed, and objects are rendered.</p>
</blockquote>
<h2 id="heading-how-it-works">How it works</h2>
<p>For FlevaR, I wanted a reliable and consistent loop.</p>
<h3 id="heading-settimeout-and-setinterval"><code>setTimeout</code> and <code>setInterval</code></h3>
<p>I first considered using <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/setTimeout"><code>setTimeout</code></a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/setInterval"><code>setInterval</code></a>, since they can call functions repeatedly with a consistent time delay.</p>
<h4 id="heading-settimeout-and-setinterval-limitation"><code>setTimeout</code> and <code>setInterval</code> Limitation</h4>
<p><em>However</em>, I noticed that the time delay was only consistent while the page was focused; anytime you left the page, each interval would take longer to be called instead of the set time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651631004494/b3rEUs-iq.JPG" alt="setInterval delay.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>With an intended fps value of 30, the expected average frame delay is <code>33ms</code> (1000 / 30); however, it jumps closer <code>1 second</code> when the game tab loses focus. Check <strong><a target="_blank" href="http://testbed.nicon.nl/timeouttest/">here</a></strong> for an interactive test.</p>
</blockquote>
<p>This reduced the reliability of <code>setInterval</code>'s consistency, since I wanted it to execute at the desired frame rate whether the tab was focused or not.</p>
<h3 id="heading-requestanimationframe"><code>requestAnimationFrame</code></h3>
<p>The second thing I learned was the browser's <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame"><code>requestAnimationFrame</code></a>. It was created for browser animations and thus was better suited for animation loops in games.</p>
<h4 id="heading-requestanimationframe-limitation"><code>requestAnimationFrame</code> Limitation</h4>
<p>Unfortunately, this method was also unreliable in moments where the tab was hidden.</p>
<h3 id="heading-custom-function">Custom Function</h3>
<p>After more research, I briefly learnt about <strong><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">Web Workers</a></strong>, which run scripts in the background and sends responses to the foreground. Fortunately, <code>setTimeout</code>s work consistently in web workers, even when the tab isn't focused. Thus, FlevaR creates a background timer (web worker) that sends a message to the foreground in consistent reliable intervals (calculated based on the fps) that calls the main loop's function. When the loop function is complete, a message is sent to the background timer which will calculate the time passed and determine how long it should take before sending another message to the foreground.</p>
<p>Assuming an fps of 25, frames should be called every 40ms from the time the loop starts: 40, 80, 120, etc</p>
<p>If the first frame called at 40ms takes 7ms, the next time out will be called to accommodate for the time difference, keeping the delay between each call as consistent as possible.</p>
<pre><code><span class="hljs-section">0ms:</span>
    Loop begins
    Background sets timeout for 40ms
<span class="hljs-section">40ms:</span>
    Timer sends message to foreground to run loop
    Loop function takes 7ms to complete
<span class="hljs-section">47ms:</span>
    Loop sends message to background that loop completed
    Background sets timeout for 33ms (80ms - 47ms)
<span class="hljs-section">80ms:</span>
    Timer sends message to foreground to run loop
    ...
</code></pre><blockquote>
<p>Due to how browsers work, the delay is never 100% accurate, but with this method it was always close and <strong>accurate enough</strong>.</p>
</blockquote>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>The developer does not actually worry about the main loop. It is handled internally from the moment the game loads. However, considering that developers may also want custom timeouts that are reliable even when other tabs are focused, FlevaR provided <code>createTimeout</code>, <code>removeTimeout</code>, <code>createLoop</code>, and <code>deleteLoop</code> functions with similar APIs to the browser equivalents (<code>setTimeout</code>, <code>clearTimeout</code>, <code>setInterval</code>, <code>clearInterval</code>). The benefit to using the FlevaR provided functions was that they were called with consistent intervals, regardless of whether the browser was focused or not.</p>
<pre><code class="lang-js"><span class="hljs-comment">// create plain "Hello world!" function</span>
<span class="hljs-keyword">const</span> myFunction = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  trace(<span class="hljs-string">"Hello world!"</span>);
}

<span class="hljs-comment">// call function every 3 seconds</span>
createLoop(myFunction, <span class="hljs-number">3000</span>);

<span class="hljs-comment">/* Output expected */</span>
<span class="hljs-comment">// &gt; "Hello world!"</span>
<span class="hljs-comment">// &gt; "Hello world!"</span>
<span class="hljs-comment">// &gt; "Hello world!"</span>
<span class="hljs-comment">// &gt; "Hello world!"</span>
<span class="hljs-comment">// ...</span>
</code></pre>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-fixed-timestep">Fixed Timestep</h3>
<p>Although reliable, the loop struggled to keep an FPS of 120 when that value was set. I'm uncertain whether it is due to the high delay of a web worker communicating with the foreground, but that could be a bottleneck when you want each frame to be called every <code>8.33ms</code> (for 120 fps). The best option to take would be to use <code>requestAnimationFrame</code>, since it was created for such consistent animations. However, in cases where I want a game to be up to date (with all scripts running consistently), this option fails when another tab is focused.</p>
<p>Gradually, <a target="_blank" href="https://gafferongames.com/post/fix_your_timestep/">through</a>- <a target="_blank" href="https://dewitters.com/dewitters-gameloop/">many</a>- <a target="_blank" href="https://gamesfromwithin.com/casey-and-the-clearly-deterministic-contraptions">gameloop</a>- <a target="_blank" href="https://codeincomplete.com/articles/javascript-game-foundations-the-game-loop/">articles</a>, I believe the best thing to do would be to call the loop as much as possible. In it, the tick methods and scripts will be called zero, one, or multiple times, depending on how long ago the last frame was called, compared to how often each frame should be called. </p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fps = <span class="hljs-number">30</span>;
<span class="hljs-keyword">const</span> step = <span class="hljs-number">1</span>/fps;

<span class="hljs-keyword">let</span> now;
<span class="hljs-keyword">let</span> dt   = <span class="hljs-number">0</span>;
<span class="hljs-keyword">let</span> last = timestamp();

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">frame</span>(<span class="hljs-params"></span>) </span>{
  now = timestamp();
  dt = dt + <span class="hljs-built_in">Math</span>.min(<span class="hljs-number">1</span>, (now - last) / <span class="hljs-number">1000</span>);
  <span class="hljs-keyword">while</span>(dt &gt; step) {
    dt = dt - step;
    tick(step);
  }
  render(dt);
  last = now;
  requestAnimationFrame(frame);
}

requestAnimationFrame(frame);
</code></pre>
<blockquote>
<p>Code used from <a target="_blank" href="https://codeincomplete.com/articles/javascript-game-foundations-the-game-loop/">this article</a>.</p>
</blockquote>
<p>That way, even if the tab loses focus, if the last frame was called <code>1000ms</code> ago when 30 frames were supposed to be called during that second, the <code>tick</code> method would be called 30 times within the while loop, and then the <code>render</code> method would be called only once after that, to reflect the latest change. It will make up for the lack of interval consistency by simulating the frames as much as possible to get back on track.</p>
<h4 id="heading-fixed-timestep-advantage">Fixed Timestep Advantage</h4>
<p>With the fixed timestep, a delta can be provided from the engine to the developer that would be used to develop deterministic games. The delta would basically be the same each frame; the engine will make the necessary steps to keep everything else consistent even when delays occur.</p>
<h4 id="heading-fixed-timestep-disadvantage">Fixed Timestep Disadvantage</h4>
<p>The only possible downside I can see with this option is the delay in an online multiplayer game. It is important to get updates from the server multiple times every frame and process them accordingly; even a drop in frame can have visible effects on the player when they leave and return to the game tab. They may have to queue updates from the server and lerp differences to maintain visual consistency when the player leaves and returns to the game tab. However, I leave that up to the developer to implement.</p>
<p>I still have more to consider before I determine a better loop to use that is both fast and reliable, so more research is necessary here.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-audio-manager">Audio Manager</h1>
<p>Depending on how the developer created the game, music is played at the menu screen, or sound effects are triggered when buttons are hovered or pressed. FlevaR's audio manager abstracts the way browsers load audio, and provides a simple API the developer can use for their games.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The audio APIs were created and modified from previous versions of games I worked on before the FlevaR game engine.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>When the developer calls the functions to create sounds in the game, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement/Audio"><code>Audio</code></a> objects are created internally. By default, the engine assumes the audio files are stored in an <code>asset</code> folder in the working directory of the application. However, developers can also provide parameters to change the path, name, and extension of the audio file.</p>
<p>When the create sound functions are called, the sources of the audio files are queued and loaded sequentially (while the game is loaded) or in the background (when the game is being played). Internally, FlevaR uses the <code>Audio</code> object's <code>onloadeddata</code> event to determine when enough of the file has been loaded and can be played.</p>
<blockquote>
<p>Some browsers do not fire the <code>onloadeddata</code> event in time for the asset, which caused errors of applications freezing on load because the event was never fired. As a result, FlevaR checks <code>onloadeddata</code>, <code>onloadedmetadata</code>, <code>oncanplay</code>, and <code>oncanplaythrough</code> events; The asset will be considered loaded based on whichever event is fired first.</p>
</blockquote>
<p>When the asset is loaded, APIs are made available to interact with the sound file (<code>play</code>, <code>clone</code>, <code>stop</code>, and more). Properties are also provided for the developer's convenience (<code>volume</code>, <code>loop</code>, and more).</p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>The default syntax to create a sound is <code>createSound(name, config)</code>. The developer first <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createsound">creates a sound clip</a>, providing a name, and if necessary, the sound's path in the working directory of the application:</p>
<h4 id="heading-example">Example</h4>
<p>Create a sound clip, using an <code>mp3</code> audio clip called <code>mySound</code> from an <code>assets</code> folder in the same directory as the <code>html</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createSound(<span class="hljs-string">"mySound"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"mySound"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"mp3"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"assets"</span> }); <span class="hljs-comment">// &lt;- either </span>

createSound(<span class="hljs-string">"mySound"</span>); <span class="hljs-comment">// &lt;- or</span>
</code></pre>
<blockquote>
<p>Note: In this scenario, the <code>config</code> object can be omitted, because if none are provided, by default, <code>type = "mp3"</code>, <code>path="assets"</code>, and the <code>name</code> would be what was provided by the developer.</p>
</blockquote>
<p>Create a sound clip, using a <code>wav</code> audio clip called <code>meow_sfx</code> from an <code>audios</code> folder in the same directory as the <code>html</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createSound(<span class="hljs-string">"mySound"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"meow_sfx"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"wav"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"audios"</span> });
</code></pre>
<p>Create a sound clip, using a free audio file from another website/url:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createSound(<span class="hljs-string">"mySound"</span>, { <span class="hljs-attr">_src</span>: <span class="hljs-string">"https://a.fakeaudiourl.com/rQIk1oe.mp3"</span> });
</code></pre>
<p>To use the sound clip, the developer can use the APIs provided by the <code>Sound Module</code>:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within scene's async onload */</span>

<span class="hljs-keyword">const</span> mySound = Sound(<span class="hljs-string">"mySound"</span>);

<span class="hljs-keyword">await</span> mySound.play(); <span class="hljs-comment">// &lt;- either</span>

<span class="hljs-keyword">await</span> Sound(<span class="hljs-string">"mySound"</span>).play(); <span class="hljs-comment">// &lt;- or</span>
</code></pre>
<blockquote>
<p>Check FlevaR's docs for more uses of the <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Module-Sound#play"><code>Sound Module</code></a></p>
</blockquote>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-deferred-playback">Deferred Playback</h3>
<p>When the developer calls <code>mySound.play()</code>, the engine tries to play the audio right away. Some browsers prevent this from happening if the tab has not been interacted with by the user, which causes the engine to return an error and crash in these instances. An internal check can be created to determine whether the player interacts with the application or not. If they have not; the audio methods can be queued, so that only when the player clicks into the screen, the queues will be resolved, the audio methods will be executed, and the browsers will not complain or crash the engine.</p>
<h3 id="heading-spacial-audio">Spacial Audio</h3>
<p>Rather than an improvement, a feature I considered adding was audio with a position in the application. The volume would increase or decrease based on the distance the audioclip was from a flevaclip or other object in game.</p>
<p>However, I realized that this functionality can be easily implemented in a flevaclip instead. Due to the nature of flevaclips being reusable components, I scrapped the implementation to allow the developer that option, as well as keep the engine as minimal as possible.</p>
<blockquote>
<p>I know I say minimal, but the engine has about 8000 single lines of code.</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-texture-manager">Texture Manager</h1>
<p>Similar to the <a class="post-section-overview" href="#heading-audio-manager">Audio Manager</a>, images are handled by FlevaR rather than the developer worrying about loading and managing them. In FlevaR, they are referred to as <code>Graphic</code> types, and all APIs used to interact with them follow a similar name.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The texture APIs were created and modified from previous versions of games I worked on before the FlevaR game engine.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>When the developer calls the functions to create graphics in the game, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image"><code>Image</code></a> objects are created internally. By default, the engine assumes the image files are stored in an <code>asset</code> folder in the working directory of the application. However, developers can also provide parameters to change tha path, name, and extensions of the image file.</p>
<p>When the create graphic functions are called, the sources of the image files are queued and loaded sequentially (while the game is loaded) or in the background (when the game is being played). Internally, FlevaR uses the <code>Image</code> object's <code>onload</code> event to determine when enough of the file has been loaded and can be displayed.</p>
<h3 id="heading-deeper-dive">Deeper dive</h3>
<p>To get a better understanding of how the graphics work (and are used in the engine), here is a sample snippet of the actual <code>Graphic</code> object in FlevaR:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651928816141/oi7F4kg1x.JPG" alt="graphic_source.JPG" class="image--center mx-auto" /></p>
<p>When the developer "creates" a <code>Graphic</code>, they can state the location and extension of the image with the <code>_path</code>, <code>_name</code>, and <code>_type</code> properties, or <code>_src</code> entirely.</p>
<blockquote>
<p><code>_src</code> can also be used as a URL for images from other websites.</p>
</blockquote>
<p>Initially, the <code>Image</code> object is created but the source is not immediately set. Instead, it is added to FlevaR's internal library of assets. A <code>loadSource</code> asynchronous function is created that sets the source of the image file which triggers the browser loading it. Upon engine load (or during synchronous or background asset checks), the <code>loadSource</code> promise is called for each asset.</p>
<blockquote>
<p>You may notice additional properties such as <code>pixelMap</code>. This property is a map of the individual pixels in the <code>Graphic</code>, which is cached internally for pixel-based collision detection.</p>
</blockquote>
<p>When the asset is loaded, resources that depend on them will be able to use them; until then, resources that rely on these assets (flevaclips) display a default placeholder provided by the engine:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651929474260/kQSFoWVDX.png" alt="default_sprite.png" class="image--center mx-auto" /></p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>The default syntax to create a graphic is <code>createGraphic(name, config)</code>. The developer first <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#creategraphic">creates a graphic</a>, providing a name, and if necessary, the graphic's path in the working directory of the application:</p>
<h4 id="heading-example">Example</h4>
<p>Create a graphic, using a <code>png</code> image called <code>myGraphic</code> from an <code>assets</code> folder in the same directory as the <code>html</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createGraphic(<span class="hljs-string">"myGraphic"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"myGraphic"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"png"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"assets"</span> }); <span class="hljs-comment">// &lt;- either </span>

createGraphic(<span class="hljs-string">"myGraphic"</span>); <span class="hljs-comment">// &lt;- or</span>
</code></pre>
<blockquote>
<p>Note: In this scenario, the <code>config</code> object can be omitted, because if none are provided, by default, <code>type = "png"</code>, <code>path="assets"</code>, and the <code>name</code> would be what was provided by the developer.</p>
</blockquote>
<p>Create a graphic, using a <code>jpg</code> image called <code>kitten</code> from an <code>images</code> folder in the same directory as the <code>html</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createGraphic(<span class="hljs-string">"myGraphic"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"kitten"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"jpg"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"images"</span> });
</code></pre>
<p>Create a graphic, using a free image from another website/url:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>

createGraphic(<span class="hljs-string">"myGraphic"</span>, { <span class="hljs-attr">_src</span>: <span class="hljs-string">"https://i.imgur.com/rQIk1oe.png"</span> });
</code></pre>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-image-atlas">Image Atlas</h3>
<p>Currently, a new object is stored in memory for each image the game has.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651931074893/awAkxDME6.JPG" alt="graphics_before_atlas.JPG" class="image--center mx-auto" /></p>
<p>One option I've considered is to create a large canvas that stores multiple images on it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651931088538/GfYWEokkf.JPG" alt="graphics_after_atlas.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>I have not checked whether one large file is better than multiple small files, but many engines go the route of storing multiple assets on one file.</p>
</blockquote>
<p>The idea would be for FlevaR draw each image onto the canvas when they have loaded, and store those dimensions as occupied. That way, when a new image is created by the developer, it will be added to the canvas at an appropriate available space, or a new large canvas would be created if it can no longer fit.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-scripting-flow">Scripting Flow</h1>
<p>An average game runs at 60 frames per second. That means scripts are executed 60 times per second, and then rendered just as fast to give the illusion and enjoyment of movement and gaming. Some scripts are called each frame, while others are only called based on a trigger, such as an object being loaded or unloaded. FlevaR provides a flow that developers can use to both maintain the simplicity of programming, and the flexibility of the declarative language involved through its APIs.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The architecture behind the way scripts work in FlevaR is inspired by <a target="_blank" href="https://expressjs.com/">ExpressJS</a> and <a target="_blank" href="https://knexjs.org/">KnexJS</a>. I do not recall what specifically inspired me, but I believe it was related to how function callbacks are handled. With ExpressJS, you define middlewares by passing functions, which are then run based on that request. Similarly, you pass functions as the parameters for other methods in FlevaR, that are then executed on a needed basis:</p>
<pre><code class="lang-js">flevaclip.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myScript</span>(<span class="hljs-params">self</span>) </span>{
    trace(<span class="hljs-string">"Hello World"</span>);
});
</code></pre>
<blockquote>
<p>The <code>myScript</code> function will be stored as a callback and executed on every frame.</p>
</blockquote>
<h2 id="heading-how-it-works">How it works</h2>
<p>The main components in FlevaR are the engine core, the scenes, and the flevaclips. When the engine loads, a method is called to use a scene previously created. When that scene is being loaded, all flevaclips in that scene are also loaded. Afterward, the scripts in each scene/flevaclip are executed each frame, until another scene is loaded (or a flevaclip is removed). Before the other scene is loaded, the current scene first unloads (calling its unload script if it exists), and all current flevaclips are also unloaded. Then the next scene and its flevaclips are loaded.</p>
<h3 id="heading-general-application-lifecyclehttpsgithubcomdanidre14flevarwikidocs-conventionslifecycle"><a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Conventions#lifecycle">General Application Lifecycle</a></h3>
<pre><code><span class="hljs-comment">// start</span>
<span class="hljs-operator">-</span> Engine load
<span class="hljs-operator">-</span> Engine tick

<span class="hljs-comment">// choose scene</span>
<span class="hljs-operator">-</span> Scene load
<span class="hljs-operator">-</span> Flevaclip load
<span class="hljs-operator">-</span> Scene tick
<span class="hljs-operator">-</span> Flevaclip tick
<span class="hljs-operator">-</span> Scene tick
<span class="hljs-operator">-</span> Flevaclip tick
<span class="hljs-operator">-</span> Scene tick
<span class="hljs-operator">-</span> Flevaclip tick
...

<span class="hljs-comment">// next scene</span>
<span class="hljs-operator">-</span> Flevaclip unload
<span class="hljs-operator">-</span> Scene unload
<span class="hljs-operator">-</span> New scene load
<span class="hljs-operator">-</span> New flevaclips load
<span class="hljs-operator">-</span> Scene tick
<span class="hljs-operator">-</span> Flevaclip tick
<span class="hljs-operator">-</span> Scene tick
<span class="hljs-operator">-</span> Flevaclip tick
...
</code></pre><h3 id="heading-general-scripting-syntax">General Scripting Syntax</h3>
<p>All scripts in the engine are passed as parameters for the engine to use as callbacks. Whenever they are executed, the engine passes the object specifically needed in that function.</p>
<h4 id="heading-for-example">For example:</h4>
<ul>
<li>when the engine loads, the engine core is provided in the engine's onload function for the developer to create the game using <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#method-summary">core specific methods</a>.</li>
<li>when the scene loads, the scene object is provided in the scene's onload function that the developer can use to add flevaclips or execute <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Scenes#method-summary">scene specific methods</a>.</li>
<li>when a flevaclip loads, the flevaclip object is provided in the flevaclip's onload function that the developer can use to <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#properties">manipulate its properties</a> and execute <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#method-summary">methods on that flevaclip</a>.</li>
<li>when a flevaclip's script is executed, the flevaclip object is provided in the callback function that the developer can use to manipulate its properties each frame.</li>
</ul>
<h4 id="heading-core-syntax">Core Syntax</h4>
<p>When the developer <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine">creates the engine</a>, they define the configurations as well as the onload method of the engine: <code>FlevaR(config, onload)</code>.</p>
<p>FlevaR passes the core object to this onload method, which is used to develop all the scenes, add all the assets, and create all the necessary scripts for the entire game.</p>
<h4 id="heading-scenes-syntax">Scenes Syntax</h4>
<p>When <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createscenename-onload">creating a scene</a>, the general format is to include a name parameter (string), and an onload parameter (function): <code>createScene(name, onload)</code>.</p>
<p>Within the onload function, the <code>scene</code> object will be passed, which can be used to access all the methods provided by a <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Scenes#method-summary"><code>Scene</code></a> object. This load function is only executed <strong>once</strong> each time the scene loads. In this function, the developer can define all other flevaclips they want in the scene. If the developer wants scripts to run each frame, they add those scripts inside an <code>addScript()</code> method provided by the <code>scene</code> object.</p>
<blockquote>
<p>Scene scripts created to run every frame are also passed the <code>scene</code> object by the engine, so more methods can be called each frame.</p>
</blockquote>
<p>Each onload function can optionally return a function that FlevaR will use for the onunload methods. When the scene is created, the onload functions are stored internally in the scene object's private <code>inits</code> (initializations) array. When the scene loads, the <code>inits</code> array is iterated and the functions are executed:</p>
<pre><code class="lang-js"><span class="hljs-comment">// scene's private load method</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">load</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> init <span class="hljs-keyword">of</span> inits) {
        <span class="hljs-keyword">const</span> fini = init(scene);
        <span class="hljs-keyword">if</span>(helperUtils.isFunction(fini) {
            finis.push(fini);
        }
    }
}
</code></pre>
<blockquote>
<p>If a function was returned on each execution, they are stored in the scene object's private <code>finis</code> (finalizations) array, similar to the syntax for code executed when a component is unmounted in ReactJS.</p>
</blockquote>
<p>The <code>scene.addScript(func)</code> method adds those functions to the scene object's private <code>scripts</code> array. These functions are executed every frame:</p>
<pre><code class="lang-js"><span class="hljs-comment">// scene's private tick method</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tick</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> script <span class="hljs-keyword">of</span> scripts) {
        script(scene)
    }
}
</code></pre>
<blockquote>
<p>The <code>script(scene)</code> executes the callback script and passes the scene object that the developer needs to access the scene's properties and methods.</p>
</blockquote>
<p>When another scene has to be loaded, the current scene first iterates through its <code>finis</code> array to call the unload methods:</p>
<pre><code class="lang-js"><span class="hljs-comment">// scene's private unload method</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">unload</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">const</span> fini <span class="hljs-keyword">of</span> finis) {
        fini(scene);
    }
}
</code></pre>
<p>Similar to the <a class="post-section-overview" href="#heading-multiple-onload-parameters">Flevaclips Syntax</a>, multiple onload functions can be created, all with their optional returned onunload functions.</p>
<h4 id="heading-flevaclips-syntax">Flevaclips Syntax</h4>
<p>Just like the <a class="post-section-overview" href="#heading-scenes-syntax">Scenes Syntax</a>, <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createprefab">flevaclips are created</a> with a parameter for their onload function: <code>createPrefab(name, props, onload)</code>.</p>
<p>Inside this function, the engine passes the flevaclip object that can be used to call all flevaclip methods. An optional return function is also available to be called when the user wants to perform additional functions when the flevaclip has been unloaded.</p>
<h4 id="heading-multiple-onload-parameters">Multiple Onload Parameters</h4>
<p>The first main goal of FlevaR was to define flevaclips once and use them as templates for future flevaclips. That way, all onload methods (as well as scripts and unload methods) from previous flevaclip templates would be called on subsequent flevaclips that were created from those templates. To get all these onload functions on the same flevaclip, FlevaR adds them to an <code>inits</code> array. For design flexibility, FlevaR also accepts multiple <code>onload</code> functions when adding the flevaclip, as subsequent parameters: <code>createPrefab(name, props, onload1, onload2, onload3, ...)</code>.</p>
<blockquote>
<p>Generally, game engines have one onload function for their entities, so I considered using a simple syntax such as <code>createPrefab(name, props, onload, onunload)</code>. However, due to the goal of reusing flevaclips as templates, I chose the route of allowing multiple onload functions, each with the option of returning a function that can be used as an onunload.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652201425120/fR1W6SvV-.JPG" alt="scripting_flow_plain.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-how-it-is-used">How it is used</h2>
<p>The general flow of a flevaclip or scene with the flexibility of multiple callbacks is up to the needs of the developer.</p>
<p>Suppose the developer wanted a scene that does the following:</p>
<ul>
<li>Outputs <strong>"Hello scene"</strong> when loaded.</li>
<li>Adds a flevaclip to the scene when loaded.</li>
<li>Outputs <strong>"Running scene"</strong> every frame.</li>
<li>Outputs <strong>"Goodbye scene"</strong> when unloaded.</li>
</ul>
<p>The related code for the above requirements would be the following:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within engine's onload */</span>
createScene(<span class="hljs-string">"myScene"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload</span>(<span class="hljs-params">scene</span>) </span>{
    <span class="hljs-comment">// Outputs "Hello scene" when loaded.</span>
    trace(<span class="hljs-string">"Hello scene"</span>);

    <span class="hljs-comment">// Adds a flevaclip to the scene when loaded.</span>
    scene.addPrefab(<span class="hljs-string">"myPrefab"</span>);

    <span class="hljs-comment">// Outputs "Running scene" every frame.</span>
    scene.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ontick</span>(<span class="hljs-params">scene</span>) </span>{
        trace(<span class="hljs-string">"Running scene"</span>);
    });

    <span class="hljs-comment">// Outputs "Goodbye scene" when unloaded.</span>
    <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onunload</span>(<span class="hljs-params">scene</span>) </span>{
        trace(<span class="hljs-string">"Goodbye scene"</span>);
    }
});
</code></pre>
<p>Suppose the developer wanted to create a prefab with <strong><em>two onloads</em></strong> that would be used in a scene, and does the following:</p>
<ul>
<li>Ouputs the x and y positions of the prefab in <strong>onload 1</strong></li>
<li>Set the x position to 75 in <strong>onload 1</strong></li>
<li>Outputs a message every frame from <strong>onload 1</strong></li>
<li>Outputs <strong>"Hello World!"</strong> in <strong>onload 2</strong></li>
<li>Outputs the x positions of the prefab in <strong>onload 2</strong></li>
<li>Outputs a message every frame from <strong>onload 2</strong></li>
</ul>
<p>The related code for the above requirements would be the following:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload1</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-comment">// Ouputs the x and y positions of the prefab in onload 1</span>
    trace(<span class="hljs-string">"X: "</span> + prefab._x);
    trace(<span class="hljs-string">"Y: "</span> + prefab._y);

    <span class="hljs-comment">// Set the x position to 75 in onload 1</span>
    prefab._x = <span class="hljs-number">75</span>;

    <span class="hljs-comment">// Outputs a message every frame from onload 1</span>
    prefab.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ontick</span>(<span class="hljs-params">prefab</span>) </span>{
        trace(<span class="hljs-string">"Running prefab from onload 1"</span>);
    });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload2</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-comment">// Outputs "Hello World!" in onload 2</span>
    trace(<span class="hljs-string">"Hello World!"</span>);

    <span class="hljs-comment">// Outputs the x positions of the prefab in onload 2</span>
    trace(<span class="hljs-string">"X: "</span> + prefab._x);

    <span class="hljs-comment">// Outputs a message every frame from onload 2</span>
    prefab.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ontick</span>(<span class="hljs-params">prefab</span>) </span>{
        trace(<span class="hljs-string">"Onload 2 frame message"</span>);
    });
}

<span class="hljs-comment">/* Within engine's onload */</span>
createPrefab(<span class="hljs-string">"myPrefab"</span>, { <span class="hljs-attr">_x</span>: <span class="hljs-number">50</span>, <span class="hljs-attr">_y</span>: <span class="hljs-number">20</span> }, onload1, onload2);
</code></pre>
<p>The final output for the above code would be the following:</p>
<pre><code><span class="hljs-comment">/* Output when the prefab loads */</span>
<span class="hljs-comment">// &gt; X: 50</span>
<span class="hljs-comment">// &gt; Y: 20</span>
<span class="hljs-comment">// &gt; "Hello World!""</span>
<span class="hljs-comment">// &gt; X: 75</span>
<span class="hljs-comment">// &gt; "Running prefab from onload 1"</span>
<span class="hljs-comment">// &gt; "Onload 2 frame message"</span>
<span class="hljs-comment">// &gt; "Running prefab from onload 1"</span>
<span class="hljs-comment">// &gt; "Onload 2 frame message"</span>
<span class="hljs-comment">// &gt; "Running prefab from onload 1"</span>
<span class="hljs-comment">// &gt; "Onload 2 frame message"</span>
<span class="hljs-comment">// &gt; ...</span>
</code></pre><blockquote>
<p>You may notice that as callbacks, the onload functions could be coded separately and added to the parameters by name. <br /> Regardless of this, the developer can <strong>still</strong> use the specific scene or flevaclip object that will be passed to it by the engine.</p>
</blockquote>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-callback-hell">Callback Hell?!</h3>
<p>If left unstructured, creating scenes, flevaclips, and more can become what would seem like nested functions inside nested functions, or callback hell, which can reduce the readability of the source code. I would try to modularize components so that they can be used with better structure and code readability.</p>
<h3 id="heading-encourage-better-code-practices">Encourage Better Code Practices</h3>
<p>Readability can also suffer if variables used are vague or repetitive:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652202967160/9a74r0-4j.JPG" alt="callback_hell_bad.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>What is self??? Python?! Why is it used everywhere but refer to different things??</p>
</blockquote>
<p><a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Overview">FlevaR docs</a> can encourage better naming conventions or function structures to increase readability:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652202971355/4Cmubjqhw.jpg" alt="callback_hell_good.jpg" class="image--center mx-auto" /></p>
<h3 id="heading-existing-engines-inspiration">Existing Engines Inspiration</h3>
<p>I initially wanted to simply create <code>onClipEvent(load)</code> and <code>onClipEvent(enterFrame)</code> APIs for FlevaR, similar to Adobe Flash AS2. However, that idea did not allow multiple functions for each event, as the current callback method I use.</p>
<p>Admittedly, when moving on from Adobe Flash, existing game engines were too overwhelming, so I did not get a thorough understanding of how their script flows work. Maybe what they do is similar to how FlevaR does it, or maybe it is drastically different (and better). Gradually, I want to make games with other engines as well and use that knowledge to better the developer experience of FlevaR, especially related to FlevaR's <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Conventions">coding conventions</a> and encouraged syntax.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-render-engine">Render Engine</h1>
<p>The game engine loads, the players clicks a button (or presses a key), and they are (usually) taken to the menu screen where they can browse the options, check the credits, or play the game. For the player to know what button to press, and whether their interactions have an effect on the application, they need visual feedback. FlevaR also handles the rendering for the developer, so they only describe where each object is positioned, what size it is, and how it looks.</p>
<h2 id="heading-how-it-works-as-well-as-how-to-use-it">How it works (as well as how to use it)</h2>
<p>All the flevaclips and scenes are drawn on the <a class="post-section-overview" href="#heading-game-screen">game screen</a> for players to see, in the order of <a class="post-section-overview" href="#heading-scenes"><code>Scene</code></a>, <a class="post-section-overview" href="#heading-virtual-camera"><code>VCam</code></a>, and then <a class="post-section-overview" href="#heading-flevaclips"><code>Flevaclip</code></a> as shown in <a class="post-section-overview" href="#heading-the-execution-system">the execution system</a>.</p>
<h3 id="heading-render-context">Render Context</h3>
<p>Currently, FlevaR uses <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D"><code>CanvasRenderingContext2D</code></a> (context 2d) APIs to draw all in-game graphics. This API is used to draw shapes, images, text, and more.</p>
<h4 id="heading-basic-context-2d-example-drawing-a-square">Basic context 2d Example: Drawing a Square</h4>
<p>To get a <code>CanvasRenderingContext2D</code> instance, you must first have an HTML5 <code>&lt;canvas&gt;</code> element:</p>
<pre><code class="lang-html"> <span class="hljs-tag">&lt;<span class="hljs-name">canvas</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"flevarCanvas"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"600"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"500"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"position: absolute; left: 0px; top: 0px;"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">canvas</span>&gt;</span>
</code></pre>
<p>To get the canvas' 2D rendering context, call <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext"><code>getContext()</code></a> on the <code>&lt;canvas&gt;</code> element, supplying <code>"2d"</code> as the argument:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> canvas = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"flevarCanvas"</span>);
<span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">"2d"</span>);
</code></pre>
<p>With the context variable, you can now draw anything. The following code draws a red rectangle on a blue screen:</p>
<pre><code class="lang-js">ctx.fillStyle = <span class="hljs-string">"#3B4050"</span>;
ctx.fillRect(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">600</span>, <span class="hljs-number">500</span>);

ctx.fillStyle = <span class="hljs-string">"#E44D26"</span>;
ctx.fillRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
ctx.strokeStyle = <span class="hljs-string">"white"</span>;
ctx.strokeRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652727498988/MWVCGrQk7.JPG" alt="drawn_rectangle.JPG" class="image--center mx-auto" /></p>
<p>So far, simple right? However, the following additional code will draw another red rectangle, this time rotated 45 degrees about the mid point of the first red rectangle:</p>
<pre><code class="lang-js"><span class="hljs-comment">// positioning to origin</span>
ctx.translate(<span class="hljs-number">200</span>, <span class="hljs-number">125</span>);

<span class="hljs-comment">// rotating matrix</span>
ctx.rotate(<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>);

<span class="hljs-comment">// repositining to source</span>
ctx.translate(<span class="hljs-number">-200</span>, <span class="hljs-number">-125</span>);

<span class="hljs-comment">// drawing</span>
ctx.fillStyle = <span class="hljs-string">"#E44D26"</span>;
ctx.fillRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
ctx.strokeStyle = <span class="hljs-string">"white"</span>;
ctx.strokeRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652727504944/BDYvpZ-SX.JPG" alt="drawn_rotated_rectangle_right.JPG" class="image--center mx-auto" /></p>
<p>The translates are necessary because context 2d uses a transformation matrix that determines where an object is drawn. If you tried rotating without first translating to the origin of the shape, you may get unintended results:</p>
<pre><code class="lang-js"><span class="hljs-comment">// rotating matrix</span>
ctx.rotate(<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">4</span>);

<span class="hljs-comment">// drawing</span>
ctx.fillStyle = <span class="hljs-string">"#E44D26"</span>;
ctx.fillRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
ctx.strokeStyle = <span class="hljs-string">"white"</span>;
ctx.strokeRect(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200</span>, <span class="hljs-number">50</span>);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652727508527/fXsXCVdVA.JPG" alt="drawn_rotated_rectangle_wrong.JPG" class="image--center mx-auto" /></p>
<p>As you realize, keeping track of all the complexities required is a tedious and slow process for any developer. That is why FlevaR handles the drawings internally, and allows the developer to simply state the positions, sizes, and <strong>appearances</strong> of flevaclips.</p>
<h3 id="heading-appearances">Appearances</h3>
<p>FlevaR has 4 appearances that can be used:</p>
<ul>
<li><a class="post-section-overview" href="#heading-graphic-appearances">Graphics</a></li>
<li><a class="post-section-overview" href="#heading-sprite-appearances">Sprites</a></li>
<li><a class="post-section-overview" href="#heading-spritesheet-appearances">SpriteSheets</a></li>
<li><a class="post-section-overview" href="#heading-painting-appearances">Paintings</a></li>
</ul>
<p>To use an appearance, a <code>setAppearance()</code> method is provided that accepts a function as a callback. In the callback, an <code>appearance</code> object (belonging to the scene or prefab) is passed and contains APIs for selecting the intended appearance based on the scene's or prefab's state.</p>
<pre><code class="lang-js">self.setAppearance(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">appearance, state</span>) </span>{
    <span class="hljs-keyword">if</span>(state.moving) {
        app.useSpriteSheet(<span class="hljs-string">"moving_char"</span>);
    } <span class="hljs-keyword">else</span> {
        app.useGraphic(<span class="hljs-string">"idle_char"</span>);
    }
}
</code></pre>
<h4 id="heading-graphic-appearances">Graphic Appearances</h4>
<p>Graphics refer to any static image the developer adds to the game, via the <code>createGraphic()</code> method:</p>
<pre><code class="lang-js">createGraphic(<span class="hljs-string">"myGraphic"</span>, { <span class="hljs-attr">_name</span>: <span class="hljs-string">"nice-image"</span>, <span class="hljs-attr">_type</span>: <span class="hljs-string">"png"</span>, <span class="hljs-attr">_path</span>: <span class="hljs-string">"assets"</span> });
</code></pre>
<p>Any intended scene or prefab can then use the graphic:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within set appearance function */</span>
app.useGraphic(<span class="hljs-string">"myGraphic"</span>);
</code></pre>
<h4 id="heading-sprite-appearances">Sprite Appearances</h4>
<p>Sprites refer to images that are dynamically created by the developer, and stored for usage. They can be <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createspritename-props-definition">created</a> using graphics, paintings, or other sprites:</p>
<pre><code class="lang-js">flevar.createSprite(<span class="hljs-string">"mySprite"</span>, { <span class="hljs-attr">_width</span>: <span class="hljs-number">100</span>, <span class="hljs-attr">_height</span>: <span class="hljs-number">120</span> }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">ctx, {_x, _y, _width, _height}</span>) </span>{
    ctx.fillStyle = <span class="hljs-string">"black"</span>;
    ctx.fillRect(_x, _y, _width, _height);
});
</code></pre>
<p>Any intended scene or prefab can then use the sprite:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within set appearance function */</span>
app.useSprite(<span class="hljs-string">"mySprite"</span>);
</code></pre>
<blockquote>
<p>Trivia: Sprites were once the only appearances available for scenes and prefabs; the developer had to add a graphic, convert it into a sprite, and then use it in an appearance method. Now, graphics can be used as appearances directly, but sprites are still kept for their dynamic creation possibilities.</p>
</blockquote>
<h4 id="heading-spritesheet-appearances">SpriteSheet Appearances</h4>
<p>SpriteSheets are sequences of images that are run on loop, giving the illusion of animation. As a simple explanation, the developer would set the size of spritesheets and how they look, using graphics, paintings, sprites, or other spritesheets. Then, FlevaR would crop and frame the generated images accordingly, and store it for use by scenes or prefabs:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within set appearance function */</span>
app.useSpriteSheet(<span class="hljs-string">"mySpriteSheet"</span>);
</code></pre>
<blockquote>
<p>For a detailed explanation on how spritesheets are defined, check the <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createspritesheetname-config-definitions">documentation</a>.</p>
</blockquote>
<h4 id="heading-painting-appearances">Painting Appearances</h4>
<p>Although FlevaR abstracts away the complexitites of rendering, some developers may still want the freedom and flexibility of using the context 2d APIs themselves. Thus, FlevaR provides "paintings" to accomplish this. Developers can define a "formula" (basically a function) which will be executed each frame, drawing the object according to the developer's code and values.</p>
<p>Furthermore, FlevaR sends the context, x, y, width, and height of the intended object to the painting function, so the developers can customize their drawings. As safeguards to prevent paintings from destroying the entire visual canvas screen, FlevaR first draws the painting on a separate canvas of matching flevaclip size, then paints that onto the main canvas screen. That way, any drawings that exceed the size of the flevaclip is automatically cropped.</p>
<blockquote>
<p>Unlike Sprites and Graphics that are stored as images, Paintings are stored as <code>Scripts</code> that are called every frame.<br /> See the <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-FlevaR-Engine#createpaintingname-formula">documentation</a> for more details on paintings.</p>
</blockquote>
<h3 id="heading-appearance-association">Appearance Association</h3>
<p>When a <code>use&lt;Appearance&gt;</code> method is called, FlevaR associates that appearance with the particular scene or prefab. When the scene or prefab needs to be rendered, FlevaR checks the appearance type, looks for the matching appearance in its library, and after calculating the position on stage (as the usual case for prefabs), draws the image or runs the painting function. Currently, appearances are only changed if the state of the prefab or scene changes, taking after how components in ReactJS are only re-rendered when state changes.</p>
<blockquote>
<p>In hindsight, this was a useless feature to use for FlevaR, as I could have simply used if statements and remove state entirely, but I need to do more <a class="post-section-overview" href="#heading-state-changes-appearances">research</a> on that.</p>
</blockquote>
<h3 id="heading-appearance-rendering">Appearance Rendering</h3>
<p>FlevaR takes the complex matrix manipulation from context 2d off the hands of the developer, so they only state where an object is placed, and how the object looks. For each prefab, FlevaR internally sets the position of the canvas matrix, then draws the specific <a class="post-section-overview" href="#heading-appearances">appearance</a>. The benefit of providing flevaclips with predefined transform properties (<code>_x</code>, <code>_y</code>, <code>_width</code>, <code>_height</code>, <code>_anchorX</code>, <code>_anchorY</code>, <code>_rotation</code>) is that the engine can use these directly when calculating the matrix transforms:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMatrixPosition</span>(<span class="hljs-params">ctx, props</span>) </span>{
    <span class="hljs-keyword">const</span> offsetX = (props._anchorX / <span class="hljs-number">100</span> * props._width);
    <span class="hljs-keyword">const</span> offsetY = (props._anchorY / <span class="hljs-number">100</span> * props._height);

    ctx.translate(props._x, props._y);
    ctx.rotate(numberUtils.degreesToRadians(props._rotation));
    ctx.translate(-(props._x + offsetX), -(props._y + offsetY));
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderFlevclip</span>(<span class="hljs-params">ctx, props</span>) </span>{
    ctx.save();

    setMatrixPosition(ctx, props);
    renderAppearance(props.appearance);

    ctx.restore();
}
</code></pre>
<p>As seen in the sample above, the engine saves the original matrix transform, calculates the transform the matrix needs to be at for the flevaclip, renders the flevaclip, and restores the matrix transform to prepare for the next flevaclip. All of this happens hundreds of times each frame, but all the player sees is the flevaclip object on the screen in its intended position, with its intended rotation, anchor, size, and appearance.</p>
<h3 id="heading-additional-scaling">Additional Scaling</h3>
<p>FlevaR also does more internally to accommodate resized game screens, increasing or decreasing the canvas matrix so other objects are scaled relatively, keeping consistency with the screen regardless of size (fullscreen, minimized, or otherwise).</p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-state-changes-appearances">State Changes Appearances</h3>
<p>FlevaR currently does not allow adding direct properties to flevaclips (such as <code>prefab.myNewVar = 24</code>) or modifying existing properties to invalid values (such as typing <code>prefab._x = "Hello world!"</code>). These decisions are to prevent any unexpected errors or engine crashes (since the internal engine will not know how to set the position of the context 2d matrix relative to a flevaclip's coordinates (<code>props._x</code>, or <code>props._y</code>) if it is a string instead of a number). However, I did know that developers would want to store additional custom values on different objects. Thus, flevaclips adopted <code>state</code> properties. Properties could be added or removed using <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#changestatekey-value"><code>changeState()</code></a>, <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#setstatestate"><code>setState()</code></a>, and <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#usestatestate"><code>useState()</code></a>, methods. When these methods are called, a flag is set internally that marks the <code>state</code> object as modified. Then, after all scripts are ran and the render methods are being called, each flevaclip checks whether its <code>state</code> has been modified, and if so, calls the <code>appearance function</code> again, which would, depending on the definition, choose an appropriate appearance visual.</p>
<p>The purpose of changing the appearance only once when <code>state</code> changes was in an effort to reduce the potential resource intensive operations that could be performed if the developer naively sought to <code>use&lt;Appearance&gt;</code> on every frame:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within flevaclip's onload */</span>
prefab.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tick</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-keyword">if</span>(prefab.state.happy) {
        prefab.useSprite(<span class="hljs-string">"happy_sprite"</span>);
    } <span class="hljs-keyword">else</span> {
        prefab.usePainting(<span class="hljs-string">"sad_painting"</span>);
    }

    <span class="hljs-keyword">if</span>(Key.isDown(Key.X)) {
    <span class="hljs-comment">// ...other code    </span>
});
</code></pre>
<p>If the above code was allowed, it would be run every frame and the appearance would be set over and over, even if the <code>state</code> did not change.</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within flevaclip's onload */</span>
prefab.setAppearance(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app, state</span>) </span>{
    <span class="hljs-keyword">if</span> (state.happy) {
        app.useSprite(<span class="hljs-string">"happy_sprite"</span>);
    } <span class="hljs-keyword">else</span> {
        app.usePainting(<span class="hljs-string">"sad_painting"</span>);
    }
});
prefab.addScript(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tick</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-keyword">if</span> (Key.isDown(Key.X)) {
        <span class="hljs-comment">// ...other code    </span>
});
</code></pre>
<p>The above code will only change the appearance once <code>state</code> changes.</p>
<h4 id="heading-the-proposed-change">The Proposed Change</h4>
<p>It would be convenient to modify the appearance directly from flevaclip events or code:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within flevaclip's onload */</span>
prefab.onClick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">prefab</span>) </span>{
    prefab.useGraphic(<span class="hljs-string">"active"</span>);
}
</code></pre>
<p>Rather than first having to set the appearance and then modifying state for the visual change to be reflected:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within flevaclip's onload */</span>
prefab.onClick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">prefab</span>) </span>{
    prefab.changeState(<span class="hljs-string">"isActive"</span>, <span class="hljs-literal">true</span>);
}
prefab.setAppearance(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app, state</span>) </span>{
    <span class="hljs-keyword">if</span> (state.isActive) {
        app.useGraphic(<span class="hljs-string">"active"</span>);
    } <span class="hljs-keyword">else</span> {
        app.useGraphic(<span class="hljs-string">"inactive"</span>);
    }
});
</code></pre>
<p>A possible change would be to remove the <code>setAppearance</code> function/barrier, and leave the <code>use&lt;Appearance&gt;</code> methods available from the scene or prefab directly. To combat the potential increased intensive operations, FlevaR can simply ignore any <code>use&lt;Appearance&gt;</code> calls internally if the flevaclip is already using that appearance.</p>
<blockquote>
<p>I'll have to do more research on this to determine a simplistic and developer friendly way to handle appearance changes.</p>
</blockquote>
<h3 id="heading-optimized-canvas-draws">Optimized Canvas Draws</h3>
<p>Some of the biggest bottlenecks that cause drops in <a class="post-section-overview" href="#heading-performance-disclaimer">engine performance</a> are <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform">translation calls</a>, as well as <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save"><code>ctx.save()</code></a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore"><code>ctx.restore()</code></a> calls being made for each flevaclip, for each frame, every second. Context 2d operations are not batched to the GPU, and are executed one by one, so while <code>ctx.save()</code> and <code>ctx.restore()</code> functions are useful, using them excessively slows down the engine.</p>
<p>Additionally, each transform function (such as <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rotate"><code>rotate()</code></a>, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/translate"><code>translate()</code></a>, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/scale"><code>scale()</code></a>) performs a matrix multiplication, and multiple multiplications increase the operations that the browser needs to perform for each flevaclip, each frame. The current <code>setMatrixPosition</code> function performs a minimum of three separate transform calls (and more calls depending on some finer ommitted details). These transforms combined with the excessive use of <code>ctx.save()</code> and <code>ctx.restore()</code> decrease the speed of the engine at scale.</p>
<p>Optimizing the renderer would thus require minimizing <code>ctx.save()</code> and <code>ctx.restore()</code> calls, as well as transform calls (and lower matrix multiplications). I've recently been experimenting with an optimized context 2d drawing method that reduces the number of matrix multiplications required per flevaclip, and does not need <code>ctx.save()</code> and <code>ctx.restore()</code> calls:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMatrixPosition</span>(<span class="hljs-params">ctx, props</span>) </span>{
    <span class="hljs-keyword">const</span> offsetX = (props._anchorX / <span class="hljs-number">100</span> * props._width);
    <span class="hljs-keyword">const</span> offsetY = (props._anchorY / <span class="hljs-number">100</span> * props._height);

    <span class="hljs-keyword">const</span> yAx = -<span class="hljs-built_in">Math</span>.sin(numberUtils.degreesToRadians(props._rotation));
    <span class="hljs-keyword">const</span> yAy = <span class="hljs-built_in">Math</span>.cos(numberUtils.degreesToRadians(props._rotation));
    ctx.setTransform(yAy, -yAx, yAx, yAy, props._x, props._y);
    ctx.transform(<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>, offsetX, offsetY);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderFlevclip</span>(<span class="hljs-params">ctx, props</span>) </span>{
    setMatrixPosition(ctx, props);

    renderAppearance(props.appearance);
}
</code></pre>
<p>There are less transform calls involved, which means less matrix multiplications. The alternatives would be to use trigonometry to calculate, as shown in responses to <a target="_blank" href="https://stackoverflow.com/questions/43882539/html-canvas-rotate-individual-shapes-on-center-axis">this stackoverflow question</a>, as well as <a target="_blank" href="https://devdreamz.com/question/758367-how-to-scale-a-rotated-rectangle-in-canvas">this very helpful article</a>. I was able to modify the code to support flevaclips of any x, y, width, height, anchor, rotation, and even scale.</p>
<blockquote>
<p>Disclaimer: Ideally I could use enough trigonometry to only need the <code>setTransform()</code> call (and not the additional <code>transform()</code> call) but due to my own math limitations, the code above was what I've currently ended with.</p>
</blockquote>
<p>The advantage of this form of canvas rendering is that it can render over 1500 objects at <strong>120fps</strong>, and over 3000 objects at <strong>60fps</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652905983611/YHx51jUyL.JPG" alt="over_1500_at_120_fps.JPG" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652905994898/lZQeYTxh1.JPG" alt="over_3000_at_60_fps.JPG" class="image--center mx-auto" /></p>
<h3 id="heading-converting-to-webgl-render-context">Converting to WebGL Render Context</h3>
<p>Browsers also provide a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API">Web Graphics Library</a> for rendering high performance interactive graphics without the use of plug-ins. Unlike <code>CanvasRenderingContext2D</code>, <code>WebGLRenderingContext</code> takes advantage of hardware graphics acceleration on the player's device. All this means is that WebGL is very fast. With it, one can render thousands of flevaclips per frame, at 120fps.</p>
<p>However, my personal knowledge of WebGL is minimal, so I could not develop a WebGL renderer for FlevaR.</p>
<h3 id="heading-using-pixijs">Using PixiJS</h3>
<p>WebGL, while powerful, is very complex. Fortunately, many libraries exist that abstract away the complexities. <a target="_blank" href="https://pixijs.com/">PixiJS</a> is the most popular and flexible 2D WebGL renderer, so I have considered using it instead of developing a WebGL renderer myself.</p>
<p>So far, everything in the engine has been created by me, based on what I've gradually learnt, and I avoided depending on external libraries for my code to work. However, if I was to begin using a WebGL renderer for an increase in speed and flexibility of FlevaR, I may consider PixiJS.</p>
<h3 id="heading-webglpixijs-caveat">WebGL/PixiJS Caveat</h3>
<p>Currently, because FlevaR uses context 2d, I can allow developers to create their custom visuals for flevaclips, through the use of <a class="post-section-overview" href="#heading-painting-appearances"><code>Paintings</code></a> where the simple context 2d API is available for them.</p>
<p>If I switch to WebGL, there are no easy APIs they can use as <code>Paintings</code>, which complicates the possibilities of FlevaR's flexible render engine when you consider current alternatives:</p>
<ol>
<li><p>Use a context 2d canvas for drawings, then render it as a WebGL texture. <br /><br /> The problem with this is that <code>Paintings</code> are executed each frame, since depending on the code the developer uses, values can be changed. The process of drawing to a 2d canvas, converting to WebGL, and drawing it on the screen, for each painting on each frame, would create a bottleneck that WebGL is supposed to solve to begin with; though <a class="post-section-overview" href="#heading-graphic-appearances"><code>Graphics</code></a>, <a class="post-section-overview" href="#heading-sprite-appearances"><code>Sprites</code></a>, and <a class="post-section-overview" href="#heading-spritesheet-appearances"><code>SpriteSheets</code></a> will be drawn faster, <code>Paintings</code>, will be slower.</p>
</li>
<li><p>Develop a similar API to context 2d that works in WebGL, and allow the developers to use that; to them, the syntax of <code>Paintings</code> will remain the same, and all appearances will benefit from the faster WebGL rendering. <br /><br />Unfortunately, although existing libraries exist that try to solve this problem (<a target="_blank" href="https://github.com/karellodewijk/canvas-webgl"><code>canvas-webgl</code></a>, <a target="_blank" href="https://github.com/play-co/webgl-2d"><code>webgl-2d</code></a>, <a target="_blank" href="https://github.com/jagenjo/Canvas2DtoWebGL"><code>Canvas2DtoWebGL</code></a>), they were never able to implement all context 2d APIs, they became just as slow as canvas to achieve similar APIs, and they were all discontinued over 5 years ago.</p>
</li>
<li><p>Develop a new API the developer can use. PixiJS already provides an <a target="_blank" href="https://pixijs.io/guides/basics/graphics.html">API</a> that can be used to draw shapes. However, that requires the developers to learn a new syntax.<br /><br />This may not be a problem currently, since FlevaR does not have many developers, but it still is something to greatly consider; I'd like to keep the engine as simple as possible for developers.</p>
</li>
</ol>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-data-storage">Data Storage</h1>
<p>One of the most exciting challenges in a game is beating a highscore or collecting the most coins in a level. To keep a player coming back to your game, you can't have the game progress resetting every time the game is reloaded. For that, it is important that the game saves the progress, and loads it at a later time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651936683236/XyAEWs3wr.JPG" alt="level_save_progress.JPG" class="image--center mx-auto" /></p>
<p>Just like other game engines, FlevaR provides an API the developer can use to allow for storing and retrieving data, called <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Module-SharedObject">SharedObjects</a>.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>The storage APIs were heavily influenced by the <strong>ActionScript 2.0</strong> APIs provided by Adobe Flash. When developing the APIs, I used <a target="_blank" href="https://flylib.com/books/en/2.93.1.64/1/">these</a>- <a target="_blank" href="https://flylib.com/books/en/3.281.1.93/1/">two</a> websites as reference.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Conveniently, all major browsers provide <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">local storage</a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB">indexedDB</a> that can be used to store and retrieve data on the user's device.</p>
<p>For efficiency and optimization, the data is often stored in the application's memory and written to storage when explicitly called by the developer. Internally, the method of storage that the engine uses (separate from the API it provides to the developer) is called a <strong>storage driver</strong>. FlevaR's SharedObjects is an abstracted wrapper around an <code>indexedDB</code> driver, and it also uses a <code>localStorage</code> driver as a fallback, where applicable.</p>
<h3 id="heading-localstorage-driver"><code>localStorage</code> Driver</h3>
<p>FlevaR uses the simple <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem"><code>localStorage.getItem</code></a>, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem"><code>localStorage.setItem</code></a>, and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem"><code>localStorage.removeItem</code></a> functions. However, by default, each application can only store up to <strong>5MB</strong> of data.</p>
<blockquote>
<p>This limit can be increased by the application user, but FlevaR tries to provide as much as possible without the user having to worry about size constraints.</p>
</blockquote>
<h3 id="heading-indexeddb-driver"><code>indexedDB</code> Driver</h3>
<p>The IndexedDB API allows rich query abilities and a larger maximum storage size than <code>localStorage</code>; only limited by the device's storage capacity, and operating system.</p>
<blockquote>
<p>I.E. It is not something the player has to worry about. For this reason, FlevaR uses <code>IndexedDB</code> as storage as the default driver.</p>
</blockquote>
<p>However, it is very complex for beginner developers. For example, you have to open a database, create an object store, start a transaction, make a request, wait for an operation, etc etc. I took the task of learning it enough to abstract that complexity away and provide it as a driver for the FlevaR engine. Thus, the developer only needs to focus on <code>SharedObjects</code>, and the engine does the efficient complex storage for them.</p>
<blockquote>
<p>To thoroughly test this driver, I also referenced the API (and source) of a JavaScript library called <a target="_blank" href="https://github.com/localForage/localForage">localForage</a>.</p>
</blockquote>
<h3 id="heading-sharedobject-module">SharedObject Module</h3>
<p>When a sharedobject is created, the developer gives it a name. Internally, an object is created in memory with the same name, and is persisted throughout the duration of the game. Depending on the code from the developer, this object is either stored to the device (<code>flush</code>), or emptied and deleted (<code>clear</code>). If multiple sharedobjects are created with the same name, the existing reference in memory is returned and shared at that variable location.</p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<h3 id="heading-creating-a-sharedobject">Creating a SharedObject:</h3>
<p>To start saving data, the developer first <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Module-SharedObject#creating-a-sharedobject">creates a SharedObject</a>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> mySharedObject = <span class="hljs-keyword">await</span> SharedObject.getLocal(<span class="hljs-string">"my_sharedobject"</span>);
</code></pre>
<p>All data to be saved is then stored in a <code>data</code> object:</p>
<pre><code class="lang-js">mySharedObject.data.username = <span class="hljs-string">"Bob"</span>;
mySharedObject.data.age = <span class="hljs-number">23</span>;
mySharedObject.data.hobbies = [<span class="hljs-string">"brawling"</span>, <span class="hljs-string">"building"</span>, <span class="hljs-string">"reading"</span>, <span class="hljs-string">"mining"</span>];
</code></pre>
<h3 id="heading-saving-a-sharedobject">Saving a SharedObject:</h3>
<p>To <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Module-SharedObject#saving-a-sharedobject">store the data</a> to the device, the developer has to explicitly call the <code>flush</code> method of the sharedobject:</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> mySharedObject.flush();
</code></pre>
<p>All the variables in the <code>data</code> property will then be stored.</p>
<h3 id="heading-deleting-a-sharedobject">Deleting a SharedObject:</h3>
<p>To <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Module-SharedObject#deleting-a-sharedobject">remove the data</a> from the device, the <code>clear</code> method of the sharedobject can be used:</p>
<pre><code class="lang-js"><span class="hljs-keyword">await</span> mySharedObject.clear();
<span class="hljs-comment">//The reference to `mySharedObject` is still active, but it no longer exists in memory, and `mySharedObject` is now empty.</span>
</code></pre>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-memory-storage">Memory Storage</h3>
<p>Both <code>localStorage</code> and <code>indexedDB</code> APIs do not work on browsers that disable cookies.</p>
<blockquote>
<p>I only discovered this when playing one of my <a target="_blank" href="https://danidre.itch.io/mountain-bell-ski">games</a> from an iframe in an incognito tab.</p>
</blockquote>
<p>In the current version of FlevaR, <code>localstorage</code> or <code>indexedDB</code> would cause the entire game to crash when the engine tries to internally access them on browsers where they are blocked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651936705509/LK3NRKQgG.png" alt="localStorage_crash.png" class="image--center mx-auto" /></p>
<p>To avoid this, I can implement a third <code>memory</code> storage driver as a fallback, that only stores the data until the application is reloaded or exited.</p>
<blockquote>
<p>This is definitely a disadvantage for players that disable cookies, so I can also create a default warning that gives them a message such as "Enable cookies to allow saving/loading to work."</p>
</blockquote>
<h3 id="heading-extensible-storage-driver">Extensible Storage Driver</h3>
<p>FlevaR aims to also run as desktop or mobile applications, and those platforms may require different storage drivers to store data on the device. For example, localStorage or indexedDB does not work in <a target="_blank" href="https://neutralino.js.org/">NeutralinoJS</a> desktop applications. However, it does provide a <a target="_blank" href="https://neutralino.js.org/docs/api/storage">storage</a> API that can be abstracted and used as a driver. Allowing FlevaR to extend the StorageObject's internals to support other APIs allows flexibility regardless of platform.</p>
<blockquote>
<p>Developers that have enough knowledge to provide a wrapper of their platform's storage API to the SharedObject can probably use that storage API for their games directly; however extending SharedObjects to support it internally can still provide flexibility to other developers that do not want to worry about each platform's specific storage APIs, and just wants to extend FlevaR's engine to support it.</p>
</blockquote>
<h3 id="heading-uncaching-idle-storage">Uncaching Idle Storage</h3>
<p>Currently, all SharedObjects persist in memory until the application has been closed. That means even if the developer destroyed the shared object variable reference, it is still available in the engine, taking up space in memory. I am considering providing a <code>free</code> or <code>unlink</code> method that the developer can use, which will not delete the data from storage, but only clear it from memory.</p>
<blockquote>
<p>If the developer wishes to use that object again, they will have to call the <code>getLocal</code> function once more.</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-flevaclips">FlevaClips</h1>
<p>Flevaclips are the main manipulative entities in FlevaR. They are pre-configured/pre-fabricated game objects that can be defined once and reusable in whatever scenes necessary. When the game loads, many of the visuals (buttons, sliders, game characters and enemies, textfields and more) are flevaclips.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<p>Before flevaclips existed, <code>Prefabs</code> was the main name for entitites. However, after research and implementing <code>Textfields</code>, I needed a term to group them both, since they had similar positions and methods. Thus, the term <code>FlevaClip</code> came about, heavily inspired after Adobe Flash's <a target="_blank" href="https://flylib.com/books/en/2.93.1.53/1/"><code>MovieClips</code></a>.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>The main properties of all flevaclips include <code>_x</code>, <code>_y</code>, <code>_width</code>, <code>_height</code>, <code>_rotation</code>, <code>_anchorX</code>, and <code>_anchorY</code>. The two current flevaclip types are <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs"><code>Prefabs</code></a> and <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Textfields"><code>Textfields</code></a></p>
<blockquote>
<p>See more information on flevaclip properties <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#Properties">here</a> (prefabs) or <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Textfields#Properties">here</a> (textfields)</p>
</blockquote>
<p>These properties define the space the flevaclip occupies on the stage. For example, if a flevaclip's <code>_x</code> and <code>_y</code> positions are at half the stage, with 100 pixels <code>_width</code> and <code>_height</code>, no <code>_rotation</code>, and offsets/anchors of 0% of the flevaclip, it will appear from top-left to bottom-right in the center of the stage:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555080585/UT1FhPfRv.png" alt="center_position_top_left_prefab.png" class="image--center mx-auto" /></p>
<p>However, if the offsets/anchors are 50% of the flevaclip, it will seem centered on the stage:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555093219/NP_EETQDy.png" alt="center_position_center_anchor_prefab.png" class="image--center mx-auto" /></p>
<p>As assumed, if the offsets/anchors are 100% of the flevaclip, it will seem centered from the bottom-right to the top-left of the stage:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555101687/gKg22BG-p.png" alt="center_position_bottom_right_prefab.png" class="image--center mx-auto" /></p>
<p>The rotation of flevaclips are also relative to the anchors:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555123640/37cvT1aSj.gif" alt="rotation_from_anchor_prefab.gif" class="image--center mx-auto" /></p>
<p>And the width and height of the flevaclip also stretches relative to the anchors:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555133016/ncwFw8CgM.gif" alt="resize_from_anchor_prefab.gif" class="image--center mx-auto" /></p>
<p>The powerful thing about the flevaclip is that it contains enough information the developer would need to describe the transform of each object on the stage. The developer simply defines <em>where</em> the flevaclip is (the shape and positions), and the engine renders accordingly (as well as determine if the mouse touches the flevaclip in that position, and more).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555143054/gr68Eo64c.gif" alt="mouse_over_prefab_or_not.gif" class="image--center mx-auto" /></p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>To create a flevaclip, you call the <code>createPrefab</code> or <code>createTextfield</code> method, depending on the desired type of flevaclip:</p>
<pre><code class="lang-js">createPrefab(<span class="hljs-string">"first_prefab"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload</span>(<span class="hljs-params">prefab</span>) </span>{
  <span class="hljs-comment">// ...other code</span>
});
</code></pre>
<p>To manipulate the properties of the flevaclip each frame, you call the desired methods and change the desired properties in a script added to the prefab:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> speed = <span class="hljs-number">5</span>;
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">prefab_movement</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-keyword">if</span> (Key.isDown(Key.LEFT))
        prefab._x -= speed;
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Key.isDown(Key.RIGHT))
        prefab._x += speed;
    <span class="hljs-keyword">if</span> (Key.isDown(Key.UP))
        prefab._y -= speed;
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Key.isDown(Key.DOWN))
        prefab._y += speed;
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">border_collide</span>(<span class="hljs-params">prefab</span>) </span>{
    <span class="hljs-keyword">if</span> (prefab._x &lt; (prefab._width / <span class="hljs-number">2</span>))
        prefab._x = (prefab._width / <span class="hljs-number">2</span>)
    <span class="hljs-keyword">if</span> (prefab._x &gt; _root._width - (prefab._width / <span class="hljs-number">2</span>))
        prefab._x = _root._width - (prefab._width / <span class="hljs-number">2</span>)
    <span class="hljs-keyword">if</span> (prefab._y &lt; (prefab._height / <span class="hljs-number">2</span>))
        prefab._y = (prefab._height / <span class="hljs-number">2</span>)
    <span class="hljs-keyword">if</span> (prefab._y &gt; _root._height - (prefab._height / <span class="hljs-number">2</span>))
        prefab._y = _root._height - (prefab._height / <span class="hljs-number">2</span>)
}

<span class="hljs-comment">/* Within prefab's onload */</span>
prefab.addScript(prefab_movement);
prefab.addScript(border_collide);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555157356/tN258RNTL.gif" alt="moveable_collidable_prefab.gif" class="image--center mx-auto" /></p>
<blockquote>
<p>See more information on prefab methods <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Flobtive-Prefabs#method-summary">here</a></p>
</blockquote>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-removing-getters-and-setters">Removing Getters and Setters</h3>
<p>Currently, many flevaclip properties are wrapped as getters and setters to control their limitations for the engine. For example, the <code>_anchorX</code> and <code>_anchorY</code> properties are locked between 0 and 100, and if the developer tries to set it to anything other than a number, it is set to 0:</p>
<pre><code class="lang-js"><span class="hljs-built_in">Object</span>.defineProperties(props, {
    <span class="hljs-attr">_anchorX</span>: {
        get() { <span class="hljs-keyword">return</span> __anchorX },
        set(_val) {
            __anchorX = numberUtils.clamp(<span class="hljs-built_in">parseFloat</span>(_val), <span class="hljs-number">0</span>, <span class="hljs-number">100</span>);
        },
        <span class="hljs-attr">enumerable</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">configurable</span>: <span class="hljs-literal">false</span>
    },
    <span class="hljs-comment">// ...other props</span>
});
</code></pre>
<p>Another example is that it allows the <code>_rotation</code> property to remain between -179, and 180, similarly to what is done with Adobe Flash's MovieClips. While convenient, this slows down the engine at scale (when hundreds of flevaclips exist at the same time).</p>
<p>Removing the getters and setters for the properties will remove the protection provided (meaning more things would break if the developer does something like <code>prefab._x = "Hello world"</code>), but it will increase the speed at scale.</p>
<blockquote>
<p>An alternative would be to enable the protection as a separate internal method in the <a class="post-section-overview" href="#heading-main-loop">main loop cycle</a> after the scripts are run, but before the objects are rendered. However, that option is open for experimentation, since developers can still break things during the script phase...</p>
</blockquote>
<h3 id="heading-re-imagining-textfields">Re-Imagining Textfields</h3>
<p>Currently, browsers only provide <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"><code>input</code></a> or <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea"><code>textarea</code></a> elements for entering text, which does not allow layering within the <code>canvas</code> element. Thus, all input boxes would appear above the canvas. FlevaR uses a <a target="_blank" href="https://danidre-canvas-textbox.glitch.me/">custom developer textbox</a> that allows the player to type directly within the canvas:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555171051/oCKweTorw.gif" alt="type_in_textbox.gif" class="image--center mx-auto" /></p>
<p>The advantage of this is that it allows input textfields to act like flevaclips with flexible depths, rotations, and more.  The disadvantage is that it does not work well for RTL text, non-latin characters, or mobile phones. This limitation is due to my own knowledge of trying to re-implement the feature in canvas instead.</p>
<p>After following along with CodeMirror's research and resources on how they implemented textfields for both desktop and mobile, I was made aware of the larger gap between what can be possible with canvas on mobile. Rather than try to re-develop textfields to support touch and drag for mobile highlighting, copy/pasting, RTL text, non-latin charactes and emojis and more, I'm considering removing the custom input in canvas and leaving it up to what is already provided by browsers.</p>
<h4 id="heading-codemirror-research-references">CodeMirror Research References:</h4>
<ul>
<li><a target="_blank" href="https://marijnhaverbeke.nl/blog/browser-input-reading.html">Browser Input Reading</a></li>
<li><a target="_blank" href="https://codemirror.net/doc/internals.html">CodeMirror Internals</a></li>
<li><a target="_blank" href="https://web.archive.org/web/20180830201622/https://codemirror.net/6/">CodeMirror Version 6</a></li>
<li><a target="_blank" href="https://web.archive.org/web/20181109055050/https://codemirror.net/6/design.html">CodeMirror Design Doc</a></li>
<li><a target="_blank" href="https://codemirror.net/6/docs/guide/">CodeMirror System Guide</a></li>
<li><a target="_blank" href="https://marijnhaverbeke.nl/blog/extensibility.html">CodeMirror Extensibility</a></li>
<li><a target="_blank" href="https://jsfiddle.net/Lfbt4c7p">Canvas Textarea Test</a></li>
</ul>
<p>FlevaR will still have layered textfields, but when they are selected, an  element will appear. The player will enter their text there and it will be populated into the layered flevaclip textfield. This would work on mobile as well, since  elements already have all the options necessary for selecting text, copy/pasting, prompting a virtual keyboard, and more.</p>
<blockquote>
<p>This is similar to how certain game engines such as the <a target="_blank" href="https://www.wickeditor.com/#/">WickEditor</a> handles text in  elements.</p>
</blockquote>
<h3 id="heading-additional-flevaclip-types">Additional Flevaclip Types</h3>
<p>Game Engines like <a target="_blank" href="https://gdevelop.io/">GDevelop</a> have multiple objects, such as <code>sprites</code>, <code>particle emitters</code>, <code>textfields</code>, <code>tiled sprites</code>, <code>tilemaps</code> and more. Rather than one object trying to support multiple options like FlevaR (static/unchangeable text, dynamic/modifiable text, input/typable text), which increases the size for each flevaclip even when those values are not being used, separating them into multiple object types can remove the bloat and let each object type serve one specific purpose. With multiple types, the engine will also be able to create optimizations, such as only rendering the visible tiles in a tiled sprite, rather than rendering every single tile if only a piece of the sprite is visible.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652555183820/sSA8Sx-Uk.png" alt="current_vs_preferred_sprite_render.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Currently, FlevaR renders the entire flevaclip even if only a piece is being displayed.</p>
</blockquote>
<h3 id="heading-extensible-flevaclip-types-plugins">Extensible Flevaclip Types (Plugins)</h3>
<p>With Adobe Flash, multiple components can be created and imported which allow additional flexibility of developers that are not provided by the engine. Entending flevaclips will allow developers to create their own flevaclip presets, methods, and render definitions, and make them engine native or distributable to other developers.</p>
<blockquote>
<p>The goal would be, specifically for tile-bsaed flevaclips, to only render displayed tiles (blue) and ignore the others (grey).</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-scenes">Scenes</h1>
<p>Generally, after the game has loaded, the player will first meet the menu screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652322556481/EPHa9ofDo.JPG" alt="rogot_slide_menu.JPG" class="image--center mx-auto" /></p>
<p>After that, they can navigate to the options screens, credit screens, game screens...different sections necessary for a game.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652322579892/hCpj1UmPK.gif" alt="game_screen_sections.gif" class="image--center mx-auto" /></p>
<p>In previous games I made (before developing the game engine), these different screens were managed by booleans or other state logic:</p>
<pre><code>const states <span class="hljs-operator">=</span> [<span class="hljs-string">"menu"</span>, <span class="hljs-string">"options"</span>, <span class="hljs-string">"level1"</span>, <span class="hljs-string">"level2"</span>];
const currentState <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
const paused <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;

<span class="hljs-keyword">if</span>(states[currentState] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"menu"</span>) {
    <span class="hljs-comment">// spaghetti menu code</span>
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(states[currentState] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"options"</span>) {
    <span class="hljs-comment">// spaghetti options code</span>
} <span class="hljs-keyword">else</span>...
</code></pre><p>However, managing the individual logics for adding and removing objects and functionality for each individual screen was always a tedious task. In FlevaR, I sought to remove that tediousness by "letting the engine handle it". Similar to existing game engines, I decided to use <code>Scenes</code>.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<blockquote>
<p>"Scenes are to FlevaR what frames are to Flash!" <br />- Danidre</p>
</blockquote>
<p>This was the first thought process when developing FlevaR. Adobe Flash used frames for all their animations, and developers used those frames as different game scenes where desired.</p>
<p>However, the first workflow of FlevaR was mostly with code in a text editor, rather than a visual editor like Adobe Flash. Due to this, I thought it very tedious trying to mimic Flash frames with code alone: having some flevaclips stuck in one frame; having others extending over multiple keyframes; having more flevaclips in higher or lower layers, each with their own nested crazy frame scenes:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652322826651/XZxNnEWCp.jpg" alt="adobe_flash_frames_and_layers.jpg" class="image--center mx-auto" /></p>
<p>I decided to separate screens by the use of <code>Scenes</code> instead of frames. The disadvantage of this is that there were no layers, but the advantage was that it was simple for developers to...develop and use.</p>
<h2 id="heading-how-it-works">How it works</h2>
<p>Internally, all scenes definitions are stored within the engine's library. When the developer decides to use a scene, it is loaded, executing all onload scripts, and adding all associated flevaclips defined in the onload scripts. Those flevaclips are then loaded as well and soon all <a class="post-section-overview" href="#heading-general-application-lifecycle">tick scripts</a> are executed each frame.</p>
<p>When the developer decides to use a new scene, the engine first unloads the current scene (as well as its associated flevaclips), then gets the next scene from its library and repeats the load process again.</p>
<h2 id="heading-how-it-is-used">How it is used</h2>
<h3 id="heading-creating-a-scene">Creating a Scene</h3>
<p>To create a scene, the developer calls the <code>createScene(name, onload)</code> method. Within the scene's onload functions, the develop declares all flevaclips they want associated with that scene, as well as the associated game logic and functionality required:</p>
<pre><code class="lang-js">createScene(<span class="hljs-string">"menu_scene"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload</span>(<span class="hljs-params">scene</span>) </span>{
    <span class="hljs-comment">// ...other code</span>
    scene.addTextfield(<span class="hljs-string">"large_text"</span>, { <span class="hljs-attr">_text</span>: <span class="hljs-string">"Mountain Bell Ski"</span> });
    scene.addPrefab(<span class="hljs-string">"play_button"</span>);
    scene.addPrefab(<span class="hljs-string">"help_button"</span>);
    scene.addPrefab(<span class="hljs-string">"fullscreen_button"</span>);
    scene.addPrefab(<span class="hljs-string">"reset_button"</span>);
    scene.addTextfield(<span class="hljs-string">"credits_text"</span>);
});
createScene(<span class="hljs-string">"help_scene"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onload</span>(<span class="hljs-params">scene</span>) </span>{
    scene.addTextfield(<span class="hljs-string">"help_text"</span>);
    scene.addPrefab(<span class="hljs-string">"menu_button"</span>);
    <span class="hljs-comment">// ...other code</span>
});
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652322879851/aJc0INMvq.JPG" alt="mountain_bell_ski_menu.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>The code above assumes the prefabs were previously created and defined using <code>createPrefab()</code> or similar methods.</p>
</blockquote>
<h3 id="heading-using-a-scene">Using a Scene</h3>
<p>To use a scene, the developer calls the <code>useScene(name)</code> method:</p>
<pre><code class="lang-js">useScene(<span class="hljs-string">"help_scene"</span>);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652322923588/uJPFOmwbO.JPG" alt="mountain_bell_ski_help.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-layers-in-layers">Layers in Layers</h3>
<p>Currently, the engine can manage scenes, and scenes can manage flevaclips. Scenes cannot manage nested scenes, and flevaclips cannot manage nested flevaclips. Along with the complexity it would add for a developer, it was also out of the scope of <strong>my</strong> personal knowledge, since I did not know how to render flevaclips nested in flevaclips if the parent flevaclip had modified dimensions:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652324821022/qZfQblUTB.png" alt="nested_prefabs_expectations_vs_reality.png" class="image--center mx-auto" /></p>
<blockquote>
<p>I also did not know how to perform mathematical calculations to determine if the mouse pointer was colliding with a nested child flevaclip, if it was distorted based on its parent's dimensions.</p>
</blockquote>
<p>It has always been a goal to allow layers in FlevaR. I've recently learnt more about matrices, so this might be a possibility soon.</p>
<h3 id="heading-a-scene-within-a-scene">A Scene within a... Scene?</h3>
<p>For game engines like Godot, all objects/nodes are basically scenes, and can have more scenes in them. I have not personally tried Godot, but the idea does sound like one I can explore with FlevaR.</p>
<h3 id="heading-visual-editor-and-frames">Visual Editor (and Frames!)</h3>
<p>Currently, I'm working on a <a target="_blank" href="https://editor.flevar.com/">Visual Editor</a> for FlevaR that allows developers to mix scripting with dragging, dropping, drawing, and more, all within the browser:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652323192423/sBr_BKH4t.JPG" alt="flevar_editor_sample.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>There's no secret to the editor: it basically generates the associated FlevaR code required for a game.</p>
</blockquote>
<p>Hopefully, even if I add complexity with layers, the visual editor will hide the tedious coding from the developer so they can have a better overall user experience. Maybe I will also be able to use frames instead of scenes?</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-virtual-camera">Virtual Camera</h1>
<p>One of the cool features a game can have is a virtual camera that follows a player:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652937775660/1Tck6xfsn.gif" alt="vcam_following_player.gif" class="image--center mx-auto" /></p>
<p>These allow the developer to create more objects on one screen, and does not limit the viewable area of the player to only that screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938295669/z02c5b9-V.gif" alt="vcam_complete_screen.gif" class="image--center mx-auto" /></p>
<p>Some game engines provide virtual cameras out of the box (Unity), and other game engines require additional components from developers (Adobe Flash). FlevaR provides a virtual camera that the developer can easily manipulate, with similar properties as flevaclips (<code>_x</code>, <code>_y</code>, <code>_width</code>, <code>_height</code>, <code>_anchorX</code>, <code>_anchorY</code>, <code>_rotation</code>), and additional properties (<code>_xScale</code>, <code>_yScale</code>).</p>
<blockquote>
<p>Trivia: The virtual camera took me over 5 months (October 2020 - March 2021) of active and inactive development, to successfully complete!</p>
</blockquote>
<h2 id="heading-how-it-works">How it works</h2>
<p>The idea of VCams in FlevaR is to capture what is directly under the virtual camera in the current scene, and project that onto the viewable screen for the player. The canvas element does not resize along with the VCam, but instead the contents of the scene are stretched to fill the screen.</p>
<p>In order to accomplish this, FlevaR internally transforms the context 2d matrix such that all future positions set by flevaclips are relative to the VCam's projection.</p>
<h3 id="heading-visual-examples">Visual Examples</h3>
<p>The following samples will give a better understanding of how the dimensions of the VCam are projected onto the resulting screen:</p>
<p>Suppose there is a 400 x 400 pixels video game with a colourful background:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938318501/Uidnk2awU.JPG" alt="vcam_exp_plain_scene.JPG" class="image--center mx-auto" /></p>
<p>If you add the default VCam, the developer would recognize no change:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938349110/fxLXWGq5o.JPG" alt="vcam_exp_default_vcam.JPG" class="image--center mx-auto" /></p>
<p>By default, a VCam is created with the same size as the canvas screen (in this case, 400 x 400 pixels), and the anchors at 0 (top left of the VCam).</p>
<p>To demonstrate positioning, suppose you set the VCam's <code>_x</code> to 150 and <code>_y</code> to 120. The following results:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938369789/gAgEMgu19.JPG" alt="vcam_exp_moved_x_y.JPG" class="image--center mx-auto" /></p>
<p>The entire context 2d matrix is shifted so you only see the pixels that the VCam is on top of.</p>
<p>Let's explore more, this time setting the anchors of the VCam to 50% (the center of the VCam):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938396456/36HNhbYTO.JPG" alt="vcam_exp_centered_anchors.JPG" class="image--center mx-auto" /></p>
<p>Although the x and y positions haven't changed, the anchors change the center of VCam rotation.</p>
<p>Rotations...let's see what would be projected if the VCam is rotated 45 degrees clockwise:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938413612/LSWRQITrq.JPG" alt="vcam_exp_simply_rotation.JPG" class="image--center mx-auto" /></p>
<p>Since the canvas element itself cannot be rotated, the entire game screen is rotated to reflect how it would look upright to the player.</p>
<p>Similarly, if we manipulate the size of the VCam, the projected view will also be stretched to fit. Let's change the <code>_width</code> and <code>_height</code> of the VCam to 250 pixels each:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938429263/0QM2eI3lE.JPG" alt="vcam_exp_shrunked_size.JPG" class="image--center mx-auto" /></p>
<p>With a smaller VCam, a smaller portion of the scene will be projected onto the screen, making the objects look larger.</p>
<p>The inverse can be observed as well; with a larger VCam, more of the scene would be projected onto the screen, making the objects seem smaller. The following example uses a VCam with 500 in size, 50% anchors, and no rotation, positioned in the center of the stage:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938458071/OerM7UCS8.JPG" alt="vcam_exp_centered_expanded_size.JPG" class="image--center mx-auto" /></p>
<p>Things get weird if you resize the VCam to a different aspect ratio than the game screen.</p>
<p>A 200 x 400 VCam on the 400 x 400 screen looks horizontally stretched:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938476410/UqDDa4q9M.JPG" alt="vcam_exp_horizontally_stretched.JPG" class="image--center mx-auto" /></p>
<p>Similarly, a 400 x 600 VCam on the 400 x 400 screen looks vertically stretched:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938491565/tEfUAP99L.JPG" alt="vcam_exp_vertically_stretched.JPG" class="image--center mx-auto" /></p>
<p>The results get even more questionable when you combine <strong>rotations</strong> with differently scaled VCams.</p>
<p>Here is the 400 x 400 stage with a VCam centered, with 200 width, 300 height, and rotated 60 degrees:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938504064/p89_lkLA-.JPG" alt="vcam_exp_wonky_rotation.JPG" class="image--center mx-auto" /></p>
<p>It looks weird, but trust me, it is projected accurately.</p>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>FlevaR provides a <code>VCam</code> module that developers can use to manipulate any of the properties of the virtual camera:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> sVal = <span class="hljs-number">15</span>;
<span class="hljs-keyword">const</span> rVal = <span class="hljs-number">3</span>;

<span class="hljs-keyword">if</span>(Key.isDown(Key.A)) {
    VCam._width -= sVal;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(Key.D)) {
    VCam._width += sVal;
}
<span class="hljs-keyword">if</span>(Key.isDown(Key.W)) {
    VCam._height -= sVal;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(Key.S)) {
    VCam._height += sVal;
}
<span class="hljs-keyword">if</span>(Key.isDown(Key.Q)) {
    VCam._rotation -= rVal;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(Key.isDown(Key.E)) {
    VCam._rotation += rVal;
}
<span class="hljs-keyword">if</span>(Key.isReleased(Key.R)) {
    VCam._rotation = <span class="hljs-number">0</span>;
    VCam._width = <span class="hljs-number">400</span>;
    VCam._height = <span class="hljs-number">400</span>;
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652945480173/f5PEEZJn8.gif" alt="vcam_exp_playthrough_3.gif" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-consistent-api">Consistent API</h3>
<p>Some naming conventions in the APIs are inconsistent, such as <code>_anchorX</code>, and <code>_xScale</code>. I originally used <code>_anchorX</code> as an additional property separate from Adobe Flash AS2's syntax, but when adding scale APIs to VCam, I used the <code>_xScale</code> as implemented in Adobe Flash AS2. For consistency, I should either choose the naming conventions as <code>_anchorX</code> and <code>_scaleY</code>, or <code>_xAnchor</code> and <code>_xScale</code>, rather than the confusing interchanged properties.</p>
<h3 id="heading-multiple-virtual-cameras">Multiple Virtual Cameras</h3>
<p>I once had many difficulties creating a split screen two player game with Adobe Flash AS2; I first needed to create duplicate movieclips for every player, enemy, and world object, and nest each player's world within a cropped version of the world to give the illusion of a split screen. It would have been much easier if I could create the world once, and add 2 virtual cameras, making one follow player 1 on half the screen, and making another follow player 2 on the other screen half.</p>
<p>Additionally, for games with minimaps or "camera" systems, it would be very powerful if the developer can use a VCam within the engine to project a portion of the world on only a portion of the screen, rather than having to create duplicate flevaclips for maps or camera objects.</p>
<p>Due to knowledge limitations at the time, I could only accomplish one VCam after 5 months of efforts, so I left it at that. However, FlevaR has plans to support multiple VCams in the future. Instead of only setting what should be projected on the screen, the developer will be able to configure what would be projected, and <strong>where</strong> on the screen it will be projected! If the developer desires, they can have the default VCam rendering the player, and additional VCams projecting a room, and displayed on numerous sides of the screen. Here is how it looks in theory:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938541653/6c1VvZSev.jpg" alt="mult_vcam_sample_all.jpg" class="image--center mx-auto" /></p>
<p>Looking complicated? Here's a breakdown of 3 virtual cameras. They can project any size, position and rotation of the scene, and can be displayed at any size, position, and rotation on the screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938554106/hfqdh-ddX.jpg" alt="mult_vcam_sample_1.jpg" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938563655/hDfsbW4lR.jpg" alt="mult_vcam_sample_2.jpg" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938575017/-QgZJAvQs.jpg" alt="mult_vcam_sample_3.jpg" class="image--center mx-auto" /></p>
<p>The ending result will have projections of each VCam at their respective locations on the screen:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938603850/YiSwnK9i7.JPG" alt="multiple_vcams_projected_result.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>As you noticed, VCams can be layered too!</p>
</blockquote>
<h4 id="heading-current-virtual-camera-development">Current Virtual Camera Development</h4>
<p>Because of recent <a class="post-section-overview" href="#heading-optimized-canvas-draws">progress/optimizations</a> I made on context 2d's draw calls, the experimental render engine can already support multiple VCams with configurable projections and views. Best of all, it still renders over 1000 objects at 60fps:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652938624444/HYCZHZ_5n.JPG" alt="multiple_vcams_rendering.JPG" class="image--center mx-auto" /></p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-event-handling">Event Handling</h1>
<p>Numerous frameworks or game engines mention event handlers (to catch when a mouse is pressed, a key is typed, or otherwise). Even Adobe Flash APIs had events such as <code>onRelease</code>, <code>onPress</code>, and more. Aside from a very hacky <code>onClick()</code> event on flevaclips, FlevaR currently has no built-in APIs that capture events and dispatch them to flevaclips or scene objects for the developer to use or modify. At the time of developing, it was not a high priority item for the engine, and later on proved too difficult to integrate with the developed architecture of the engine.</p>
<p>One example of the difficulty is trying to add a <code>core.onMousePress</code> event. Browser events, for example, has a <code>target</code> property that stores the element that received the event, so instead of adding 100 <code>onMousePress</code> events to each element, the developer can set 1 <code>onMousePress</code> event and use the <code>target</code> property to determine which of the 100 elements was pressed.</p>
<p>However, Flevaclips can be any determined sizes or positions, and can change their transform very dynamically. Thus, if FlevaR had to determine which flevaclip the mouse hovered each frame, it would need to loop through all flevaclips on the stage. FlevaR had no existing way to optimize flevaclips in chunks or BSTs based on fixed sizes, because the developer can alter the transforms such that the chunk sizes would not be compatible. This is also a problem because depending on the size of a flevaclip, its <code>_x</code> and <code>_y</code> positions can be off the visible screen, but its <code>_rotation</code> and size would be such that it still crosses the view, hence it will need to be checked for mouse events as well.</p>
<blockquote>
<p>Chunk-based optimizations such as that is left up to the individual developer, based on their particular game; they will know what constraints to use</p>
</blockquote>
<p>To get around this currently, instead of looping all flevaclips for mouseovers, FlevaR only loops over flevaclips that the developer explicitly added a <code>onClick()</code> event to; this saves loop resources each frame.</p>
<blockquote>
<p>I'll have to do more research to find an optimized way to loop these things regardless of their positions, sizes, offsets, and rotations; for hundreds of flevaclips, many times per second, at performant speed.</p>
</blockquote>
<p>Currently, the goto APIs provided are simply <a class="post-section-overview" href="#heading-mouse-inputs"><code>Mouse</code></a> and <a class="post-section-overview" href="#heading-keyboard-inputs"><code>Key</code></a> modules and their <code>isUp</code>, <code>isDown</code>, <code>isPressed</code>, and <code>isReleased</code> methods.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-root-proxy">Root Proxy</h1>
<p>One of the most powerful <strong>(but slow)</strong> APIs FlevaR provides is the <code>_root</code> property. It is defined as the engine's stage, used to reference stage properties and methods, or flevaclips within the main <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki/Docs-Conventions#lifecycle">lifecycle</a> of the application.</p>
<h2 id="heading-what-influenced-it">What influenced it</h2>
<h4 id="heading-adobe-flash-adobe-flash-adobe-flash">Adobe Flash. Adobe Flash. Adobe Flash.</h4>
<p>Adobe Flash's AS2 API provided a <code>_root</code> object that gave the developer access to many properties in the application:</p>
<ol>
<li>If you gave a movieclip an instance name of <code>"character"</code>, you can access it with code through <code>_root.character</code>.</li>
<li>If you wanted to get the mouse positions, you can access them with <code>_root._xmouse</code> and <code>_root._ymouse</code>.</li>
<li>If you wanted to use core methods or go to other frames, you can with root (<code>_root.gotoAndStop()</code>).</li>
<li>If you redefined an existing accessible object (<code>_root._xmouse = "Hello world"</code>) it would be ignored.</li>
<li>If you tried to access a non-existent root property (<code>_root.noProp</code>) or nested root property (<code>_root.my.values.do.not.exist</code>), Flash would simply return <code>undefined</code> instead of throwing an error.</li>
</ol>
<h2 id="heading-how-it-works">How it works</h2>
<p>I wanted to also provide a <code>_root</code> property with the flexibility allowed in AS2. The best way <em>(or rather, only way)</em> to do that in JavaScript was using a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"><code>Proxy</code></a> object. These objects intercept and redefine fundamental operations for the object.</p>
<p>The FlevaR proxy is a very complicated one; when trying to access <em>(get)</em> a property, the following sequence is followed:</p>
<ol>
<li>If <code>toString</code> is accessed (<code>_root.toString()</code>), return the string <code>"[root Stage]"</code>.</li>
<li>If reserved properties (<code>_root.type</code>) is accessed, return their value.</li>
<li>If a string matching an existing flevaclip's instance name (<code>_root.character</code>) is accessed, return the flevaclip and/or property of the flevaclip (<code>_root.character._x</code>).</li>
<li>If a property of the stage (<code>_width</code>, <code>_color</code>, etc) is accessed (<code>_root._color</code>), return the value.</li>
<li>If a method of the stage (<code>attachPrefab()</code>, <code>removePrefab()</code>, etc) is accessed (<code>_root.attachTextfield()</code>), call the method on the stage.</li>
<li>If a string matching no existing property was accessed (<code>_root.noProp</code>), return <code>"undefined"</code>.</li>
<li>If a non-existent object was accessed, return a nested root proxy that just returns <code>"undefined"</code> instead of errors for missing objects and missing object properties. It treats each new key in a nested object as another proxy object, so <code>_root.my.values.do.not.exist</code> would return <code>"undefined"</code> instead of <code>Error: cannot access "values" of undefined object: "my"</code> as is usually expected in JavaScript.</li>
</ol>
<blockquote>
<p><strong>Trivia:</strong> The nested root proxy on step 7 of the sequence is called a <code>floxyChain</code> for <code>FlevaR Proxy Chain</code>. See the entire internal code for FlevaR's _root proxy <a target="_blank" href="https://github.com/danidre14/FlevaR/blob/ac90c51a0862a00661521333f3c75a942c795d6e/FlevaR.js#L7573">here</a> (Line 7573 of the engine)</p>
</blockquote>
<p>A similar sequence is followed when modifying <em>(set)</em> a property:</p>
<ol>
<li>If reserved properties are modified (<code>_root.type = "flobtives"</code>), silently ignore.</li>
<li>If a string matching an existing flevaclip's instance name (<code>_root.character = "Hello FlevaR!"</code>) is modified, silently ignore.<br />The code <code>_root.character._x = 5</code> will work because <code>_root.character</code> <em>gets</em> the flevaclip, and <code>flevaclip._x = 5</code> modifies it as expected.</li>
<li>If a property of the stage (<code>_width</code>, <code>_color</code>, etc) is modified (<code>_root._color = "green"</code>), set the value of the stage property.</li>
<li>If a method of the stage (<code>attachPrefab()</code>, <code>removePrefab()</code>, etc) is modified (<code>_root.attachTextfield = 21</code>), silently ignore.</li>
<li>If a non-existent object is modified (<code>_root.my.values.do.not.exist = "yes I do exist now"</code>, silently ignore.</li>
<li>Silently ignore anything otherwise.</li>
</ol>
<h3 id="heading-root-order-priority">Root Order Priority</h3>
<p>As noticed, the simplified order priority when accessing or modifying root properties is as follows:</p>
<ul>
<li>FlevaClips</li>
<li>Stage Properties</li>
<li>Stage Methods</li>
</ul>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>Let's start with an empty unmodified scene:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652567916539/aOC7YTVR7.JPG" alt="scene_empty_unmodified.JPG" class="image--center mx-auto" /></p>
<p>Change the stage's colour to a blue colour:</p>
<pre><code class="lang-js">_root._color = <span class="hljs-string">"#3B4050"</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652567930114/Dv2Q0YqBx.JPG" alt="scene_color_a_blue.JPG" class="image--center mx-auto" /></p>
<p>Add a prefab with instance name of <code>"myCoolPrefab"</code> to the stage:</p>
<pre><code class="lang-js">_root.attachPrefab(<span class="hljs-string">"my_prefab"</span>, { <span class="hljs-attr">_x</span>: <span class="hljs-number">45</span>, <span class="hljs-attr">_y</span>: <span class="hljs-number">60</span>, <span class="hljs-attr">instanceName</span>: <span class="hljs-string">"myCoolPrefab"</span>, <span class="hljs-attr">attachedName</span>: <span class="hljs-string">"myAttachedClip"</span> });
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652567948633/65QciBgLS.JPG" alt="scene_added_prefab.JPG" class="image--center mx-auto" /></p>
<p>Modifying the attached prefab:</p>
<pre><code class="lang-js">_root.myCoolPrefab._x = <span class="hljs-number">100</span>;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652567965314/j8dl6G_KE.JPG" alt="scene_modified_prefab.JPG" class="image--center mx-auto" /></p>
<p>Removing the attached prefab:</p>
<pre><code class="lang-js">_root.removePrefab(<span class="hljs-string">"myAttachedClip"</span>);
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652567983511/S-EnCATTR.JPG" alt="scene_removed_prefab.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>As noticed, the prefab is located for removal through the <code>attachedName</code> string.</p>
</blockquote>
<p>Due to the <a class="post-section-overview" href="#heading-root-order-priority">order priority</a>, giving flevaclips instance names that conflict with stage properties/methods causes the <code>_root</code> accessor to reference the flevaclips instead. This is consistent with Adobe Flash AS2's APIs as well. Thus, assuming a flevaclip on stage has <code>instanceName: "_color"</code>:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* Within scene's onload */</span>

<span class="hljs-comment">// add flevaclip to scene</span>
scene.addPrefab(<span class="hljs-string">"my_prefab"</span>, { <span class="hljs-attr">_x</span>: <span class="hljs-number">150</span>, <span class="hljs-attr">instanceName</span>: <span class="hljs-string">"_color"</span> });
</code></pre>
<p>Attempting to change the scene's <code>_color</code> will silently fail:</p>
<pre><code class="lang-js"><span class="hljs-comment">// attempt changing stage's color to blue</span>
_root._color = <span class="hljs-string">"green"</span>; <span class="hljs-comment">// &lt;-- silently fails</span>

<span class="hljs-comment">// trace _color property in _root</span>
flevar.trace(_root._color);

<span class="hljs-comment">// &gt; [type Prefab]</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652568370163/vTD9CZhJ7.gif" alt="flevaclip_overwrite_root_color_small.gif" class="image--center mx-auto" /></p>
<h2 id="heading-how-i-would-improve-it">How I would improve it</h2>
<h3 id="heading-convert-to-root-object">Convert to Root Object</h3>
<p>Rather than a slow proxy, an object should be used to store the dynamic values.</p>
<p>Currently, any stage properties are accessed from an internal <code>Stage</code> module, which also uses getters and setters to prevent bugs (in case the developer types <code>stage._width = "hello world"</code>, crashing the game, since many of the internal components in the engine depends on the width of the stage being a number value). A proxy <strong>on top</strong> of getters and setters means an even slower engine. I would optimize this by placing the properties and methods directly on a <code>_root</code> object, and accessing them there internally as well.</p>
<p>Currently, root loops through all active flevaclips to see if an instance name matches the desired key being accessed on the root. To optimize this, flevar can cache flevaclips by internally adding all flevaclips with instance names to a dictionary/bucket of the root object. Thus, checking for an existing flevaclip by instance name would check the cache immediately and remove the loop that increases with the number of active flevaclips.</p>
<blockquote>
<p>Although these optimizations can remove the proxy, getters and setters, and increase the engine performance at scale, the developer would no longer be as protected as before: bad code such as <code>_root._color = 5;</code> and <code>_root.my.values.do.not.exist</code> would cause unexpected results or crash the engine with errors. Such is the sacrifice to be made for speed and optimizations.</p>
</blockquote>
<h3 id="heading-separate-into-distinct-modules">Separate into Distinct Modules</h3>
<p>Unless accustomed to by developers, having one object store properties for separate modules, and depending on the developer to remember priorities and possible overwrites, while flexible, can become tedious. Separating properties into more explicit modules can help:</p>
<ul>
<li>Put all stage properties and modules into a provided <code>Stage</code> API: (<code>Stage._wdith</code>, <code>Stage._colour</code>, <code>Stage.attachPrefab()</code>, etc)</li>
<li>Put all mouse properties in its <code>Mouse</code> Module: (<code>Mouse._x</code>, <code>Mouse._y</code>)</li>
<li>Use explicit functions for accessing flevaclips: (<code>getFlevaclip("myCoolPrefab")</code>)</li>
</ul>
<blockquote>
<p>The <code>_root</code> object may stick around for convenience, but maybe the module separation can improve explicitness and developer experience.</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-performance-disclaimer">Performance Disclaimer</h1>
<p>Numerous times I may mention a feature being <em>"slow at scale"</em> or <em>"affecting performance"</em> in the engine. Do note that these issues currently only arise when there are hundreds of flevaclips active at once:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652564055697/SvI8EDSkC.JPG" alt="stress_test_1000_plus_clips.JPG" class="image--center mx-auto" /></p>
<p>FlevaR can handle an average of 400 active flevaclips at 60ps, which is much more active flevaclips than an average scene would need:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652564065405/tbQYywxSa.JPG" alt="stress_test_400_plus_clips.JPG" class="image--center mx-auto" /></p>
<p>These drawbacks are due to multiple reasons, such as:</p>
<ul>
<li>the canvas2d <a class="post-section-overview" href="#heading-render-engine">render limitations</a></li>
<li>the powerful but slow <a class="post-section-overview" href="#heading-root-proxy">root proxy</a> object</li>
<li>general bad design (spaghetti code) of an earlier/naive Danidre developing the engine</li>
</ul>
<p>Those caveats can be mitigated though, but that currently depends on the individual skills of the developer. The example below uses paintings and only considers the position of the object, not the width nor rotation. Thus, no transforms are required, allowing up to 4000 objects rendering per frame, with 60fps:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652564070526/bra_PqBo0.JPG" alt="stress_test_4000_plus_clips.JPG" class="image--center mx-auto" /></p>
<blockquote>
<p>If they want to create a <strong>Shoot 'em up</strong> game, they can use one flevaclip with a painting for all bullets, rather than thousands of flevaclips for each bullet.</p>
</blockquote>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-flevars-future">FlevaR's Future</h1>
<p>The journey away from Adobe Flash took me on a learning journey and I had the most fun developing the game engine.</p>
<p>Funny enough, only after I created my engine, I discovered many existing JavaScript game engines and frameworks, such as <a target="_blank" href="http://craftyjs.com/">CraftyJS</a>, <a target="_blank" href="https://www.createjs.com/">CreateJS</a>, <a target="_blank" href="https://impactjs.com/">ImpactJS</a>, <a target="_blank" href="https://github.com/pbs/Platypus">Platypus</a>, <a target="_blank" href="https://www.melonjs.org/">MelonJS</a>, <a target="_blank" href="https://www.wickeditor.com/#/">Wick Editor</a> and so much more! Before that, I could have only found <a target="_blank" href="https://phaser.io/">Phaser</a> and <a target="_blank" href="https://pixijs.com/">PixiJS</a>, so I thought those were the only existing JavaScript game engines or frameworks.</p>
<p>The variety in JavaScript game engines would get one thinking, why use FlevaR? Why not use an already existing game engine? <em>I ask myself that, too!</em> However, most of the frameworks listed are archived/outdated/discontinued. Additionally, FlevaR's goal is not to compete with established game engines such as Phaser. Rather, it aims to be a declarative JavaScript game engine, with APIs similar to Flash. It targets beginners and promotes a simple easy environment for developing applications. While being friendly, FlevaR's APIs also allow for complex applications if the developer needs it, and with JavaScript as the main language, it is easy for existing web developers to pick up and use it! All the abstractions from the browser and internal modules I stitched together is to make <em>that</em> possible. <a class="post-section-overview" href="#heading-flevars-motto"><strong><em>FlevaR provides; Developer decides.</em></strong></a></p>
<h2 id="heading-future-plans">Future Plans</h2>
<h3 id="heading-engine-re-write">Engine Re-Write</h3>
<p>A FlevaR re-write is definitely planned, where I explore all the proposed optimizations for different modules. I'm currently in university, so the re-write may be a year-long process.</p>
<h3 id="heading-flevar-editor">FlevaR Editor</h3>
<p>I've since been working on a <a target="_blank" href="https://editor.flevar.com/">visual editor</a> for FlevaR games, that can transpile applications to the browser or as a desktop app. Many of the visual examples in this article was made easier through the drag-and-drop editor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652971091492/946MQPPxU.png" alt="FlevaR Editor.png" class="image--center mx-auto" /></p>
<blockquote>
<p>Note: The editor is in heavy development!</p>
</blockquote>
<h3 id="heading-port-flash-games">Port Flash Games</h3>
<p>The original goal was for me to learn to make games in a new language/engine, and port my <a target="_blank" href="https://danidre14.newgrounds.com/games">old Flash games</a>. Currently, a Flash Player emulator called <a target="_blank" href="https://ruffle.rs/">ruffle</a> exists, allowing my old Flash games to run in web browsers once more. However, it is still a future goal of mine to remake my Flash games in FlevaR.</p>
<h3 id="heading-develop-flevar-games">Develop FlevaR Games</h3>
<p>Although I work on the engine, I still want to make games, so I plan on using FlevaR for many more games. That way I can continue to test the user experience of the engine or fix bugs that may be discovered.</p>
<blockquote>
<p>Until I get a team dedicated to finding bugs or assisting with engine development, I'll have to be both developer and tester!</p>
</blockquote>
<h3 id="heading-flevar-for-androidios">FlevaR for Android/iOS</h3>
<p>I created a <a target="_blank" href="https://www.patreon.com/danidre">Patreon</a> for the development of FlevaR. With the remake of FlevaR, I also want to develop APIs to support touchscreen, so applications can work on mobile phones. I also want to provide services that can package FlevaR applications for Android and iOS devices.</p>
<h3 id="heading-flevar-website-portal-and-additional-apis">FlevaR Website Portal and Additional APIs</h3>
<p>One goal is to provide a <strong>portal</strong> for FlevaR web applications, similar to portals that exist for games made with <a target="_blank" href="https://godotengine.org/showcase">Godot</a>, <a target="_blank" href="https://gamemaker.io/en/showcase">GameMaker</a>, or <a target="_blank" href="https://liluo.io/">GDevelop</a>. Users will be able to view and rate other FlevaR games, or fork the <strong>source code</strong> of games if permitted by the developer. Additionally, FlevaR APIs such as <strong>highscores</strong> and <strong>medals</strong> will be implemented to take advantage of the user system and bring more <strong>rewards</strong> for both FlevaR developers and players.</p>
<p><em><a class="post-section-overview" href="#heading-contents">back to contents</a></em></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>FlevaR has come a long way, and still has a long way to go; I am passionate about developing this engine and learning more every step of the way.</p>
<p>This article could not be done without the <a target="_blank" href="https://townhall.hashnode.com/the-epic-hashnode-writeathon">Epic Hashnode Writeathon</a>, which encouraged me to write my dream article; this is something I've put off for almost 2 years!</p>
<p>This article was quite a read, so thank you for making it to the end. Hopefully it shares coherent insight on how you can use browser APIs for your own purposes. Maybe you do not need an entire engine; maybe you only need an asset manager, or a rendering system.</p>
<p>Regardless, I hope this article can serve as a gem for future website explorers or gamedev enthusiasts, just like many articles before this one helped FlevaR get to where it is!</p>
]]></content:encoded></item><item><title><![CDATA[Ludum Dare 46 Results]]></title><description><![CDATA[Game Engine
It all started when I brainstormed a declarative game engine that'd be based on callbacks and contain similar features to Flash. I went straight into development. Out came FlevaR, my custom game engine.
The name idea came from "FlevaPack"...]]></description><link>https://blog.danidre.com/ludum-dare-46-results</link><guid isPermaLink="true">https://blog.danidre.com/ludum-dare-46-results</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Games]]></category><category><![CDATA[danidre]]></category><category><![CDATA[ludumdare46]]></category><category><![CDATA[ldjam]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Thu, 14 May 2020 17:19:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660065594482/A54HAnBan.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-game-engine">Game Engine</h1>
<p><em>It all started</em> when I brainstormed a declarative game engine that'd be based on callbacks and contain similar features to Flash. I went straight into development. Out came <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki">FlevaR</a>, my custom game engine.</p>
<p>The name idea came from "FlevaPack", a mini API for easily manipulating and auto resizing <strong>DOM</strong> elements. Since I decided to turn it into an entire library, I called it <strong>Flevapack Revolution(alized)</strong>; or FlevaR.</p>
<p>Coincidentally, I found out that the next <a target="_blank" href="https://ldjam.com/">Ludum Dare Jam</a> was approaching in one week, so the "meme" became whether or not I can create a game with the engine. The race to develop the main features before the Jam begins was on! Audio manager, textures, spritesheets, objects, built-in collision...all spaghetti coded throughout the week until at last, Ludum Dare 46 was upon us.</p>
<h1 id="heading-game-jam">Game Jam</h1>
<p><a target="_blank" href="https://itch.io/jam/geta-game-jam-11">Geta Game Jam</a> was also occuring throughout the same weekend; thus the meme was to create a game that'll possibly fit both jam themes. But..I needed a team.</p>
<p>I submitted a <a target="_blank" href="https://ldjam.com/events/ludum-dare/46/miscen-again/ludum-dare-is-here-again-ill-continue-my-trend-team-inquiry-included">last minute post</a> explaining my situation (trying a new "engine", only starting 24 hours into the jam, leaving me with 48 hours for the jam) and was able to get an artist and a music composer. Anxiety spiked, since I feared the worst! (getting great assets but making a terrible game) This was no longer a meme; this was serious.</p>
<h2 id="heading-theme">Theme</h2>
<p>Ludum Dare's theme was <strong>"Keep it Alive"</strong>.</p>
<p>Geta Game Jam's theme was <strong>"Something is Missing"</strong>.</p>
<p>So we had to create a game that merged those two themes! 😃</p>
<h2 id="heading-brainstormingdevelopment">Brainstorming/Development</h2>
<p>The brainstorm session began. I went as far as using idea generators, reading blogs, and finally, bringing my high school friend onboard. I needed someone to work on gameplay and design, while I focus on programming, so that was his task. But throughout the project, it still felt like ultimately, I was the director, saying "yes" and "no" to assets, and requesting opinions on everything. Saying "I need this", or "Can you get me that" instead of being told "The game should do this."</p>
<h3 id="heading-cons">Cons</h3>
<p>One limitation was that most of the considerations were always "could your engine handle/support this". A small con though, since it was actually able to handle everything thrown at it (with a few tweaks here and there).</p>
<p>Another con was that, since I was occupied 24/7, there was no one to focus on posting "progress" screenshots to build hype of the game while in development.</p>
<p>Ideally, I wanted to work on the game while other members focused on setting up the submissions page and all that, but in the end I lacked the confidence to ask anyone to do things out of their talents, so I ended up doing it myself, last minute.</p>
<p>As a result, I ran out of time with the game, and had to rush adding menu screens and more during (and a bit past) submission hour.</p>
<h3 id="heading-pros">Pros</h3>
<p>The artist was able to whip out some amazing levels design. And after working on the mechanics for most of the jam, adding the graphics was a breeze.</p>
<p>The music composer's music was mindblowing! Easily able to adjust the music and increase intensity and chaos, or include soothing ease where required.</p>
<p>The designer came through with many riddles to implement, in sync with the assets provided. And, I was able to split some of the programming work (such as collision) to him, so I had more time to work on other things.</p>
<p>I've never actually known about the many different resources that goes into making something, and I appreciate every aspect of it now.</p>
<h1 id="heading-results">Results</h1>
<p>The results were...underwhelming...<strong>AT FIRST!!</strong></p>
<p><img src="https://i.imgur.com/xqWV9Eh.jpg" alt="results picture" class="image--center mx-auto" /></p>
<p>But expected, I tell you! 😅</p>
<h2 id="heading-reasonsexcuses">Reasons/Excuses</h2>
<p>I call them excuses since that's how I analyzed them to make myself feel better:</p>
<p>1) It turns out, <strong>MANY</strong> developers decided to participate in the jam this event. Compared to the usual <strong>~5000</strong> signups and <strong>~3000</strong> games, this Ludum Dare saw <strong>~10000</strong> signups and <strong>~5000</strong> games. </p>
<h3 id="heading-ld45-statistics">LD45 Statistics</h3>
<p><img src="https://i.imgur.com/tFWjt2d.jpg" alt="ld45statistics" class="image--center mx-auto" /></p>
<h3 id="heading-ld46-statistics">LD46 Statistics</h3>
<p><img src="https://i.imgur.com/42m3Bax.jpg" alt="ld46statistics" class="image--center mx-auto" /></p>
<p>That means I had <strong>ALOT</strong> of competition. Maybe more elite teams joined in for the event. 😄</p>
<p>2) It's a result of a game I made in a <em>meme engine</em>. How far did I expect to get with it?</p>
<p>3) There were many limitations and inevitably, I ran out of time. There're many things I wanted to add to it (and still do) but it's better left as it is.</p>
<h2 id="heading-comparisonsgotchas">Comparisons/Gotchas</h2>
<p>Compared with my previous game:</p>
<p>Bob the Brawler placed <strong>#541</strong>st overall out of 1885 Jam entries, being ranked in the top <strong>28.7%</strong> of games.</p>
<p>Miscen...Again!? placed <strong>#1131</strong>st overall out of 3576 Jam entries, being ranked in the top <strong>31.6%</strong> of games.</p>
<p>That's a small drop in relative placements.</p>
<p>Similarly, looking at scores only, <strong>Miscen...Again!?</strong> beat <strong>Bob the Brawler</strong> in every category except <strong>Fun</strong>.</p>
<p><img src="https://i.imgur.com/hM4pXVg.jpg" alt="compare scores picture" class="image--center mx-auto" /></p>
<p>I don't blame it, a <em>riddle</em> game doesn't have much action, compared to a <em>beat-em-up quick-paced-brawler</em>.</p>
<p>My highest score was the audio! With <strong>3.815 stars!</strong> I placed in the top <strong>10%</strong> of games in that category!</p>
<p><img src="https://i.imgur.com/t42KfMT.jpg" alt="compare standings grid picture" class="image--center mx-auto" /></p>
<p>The art was a close one too! BtB has a great artist as well, that explains why the ratings were so close!</p>
<h2 id="heading-honest-disadvantages">Honest Disadvantages</h2>
<p>To be honest, I was pretty bummed out about last Ludum Dare's results, where I played and played and rated lots of games, obtaining 100+ reviews on my game, which all seemed to be positive reviews. Yet, the ratings at the end didn't reflect all the positive feedback. I was <a target="_blank" href="https://danidre.herokuapp.com/posts/view/ludum_dare_45_results#deceptive-much">betrayed</a> by developers who wanted good ratings on their games! 🙃</p>
<p>As such, I concluded, I'll just play the minimum needed to get 20 reviews, then leave it there until results day to see if I do better this time. And even if I tried, I couldn't feel the excitement to play and rate games every single day; so I left it at that.</p>
<p>So, it is an illusional victory to compare <strong>Bob the Brawler</strong>'s 114 ratings to <strong>Miscen...Again!?</strong>'s 29 ratings. Who knows, maybe the average stars would have lowered if I received 100+ more? Even though I tried to "cheat" the system, it didn't change the outcome much, which I conclude is a reflection of the game itself.</p>
<p>Well, it doesn't matter anyways. I'm not hung up on the results anyways since it was a mix between two jams. I was really excited participate in a team this Jam, and am really contented with what I was able to bring out of it. I figured I'd complete it before uploading it to other game portals (such as Newgrounds), but in conclusion, I'll leave it as it is, flaws and all.</p>
<p>I am satisfied.</p>
<hr />
<h1 id="heading-sowhats-next">So...what's next?</h1>
<h2 id="heading-engine">Engine</h2>
<p>During the 3 weeks of ratings and playing games, I've been working on expanding the <a target="_blank" href="https://github.com/danidre14/FlevaR">FlevaR Engine</a>. It's evolved into more than a meme. I was able to retrieve the files of my old flash games from my <a target="_blank" href="https://danidre14.wixsite.com/danidre14">first wix website</a>, and that further motivated me to complete the engine.</p>
<p>I plan to further develop it and port some of my old <strong>Flash</strong> games to <strong>JavaScript</strong>, hopefully before 2020 ends. As well, I'll need to standardize the engine and complete the first official rollout of the <a target="_blank" href="https://github.com/danidre14/FlevaR/wiki">wiki</a> so other interested developers can get started with it.</p>
<h2 id="heading-collaboration">Collaboration</h2>
<p>I prefer to have someone onboard to do the world design and story building or giving meaning behind games. I understand that requires a lot of creativity and time. So in future projects, I'll also look for someone with that skill. As for game hype during development, I may also need to look for a <strong>marketer</strong> (someone solely responsible for the social media setups and advertising of the game during development and a while after release).</p>
<p>In my past collaborations, my role was basically <em>"Give Danidre the assets, he'll handle everything else forever, bye."</em></p>
<p>Hopefully, I can maintain communication (if even a little bit) with team members and hopefully my role becomes more like <em>"Create assets and give to 'the team' to incorporate it with other assets and designs and merge into one game"</em>...unless of course the general idea is to spontaneously <strong>join, create, leave</strong>. 😂</p>
<p>Because it felt like I hired freelancers...for free, rather than briefly joined a team and built something. Hopefully I can find an area/create an environment in the future where responsibilities are more evenly distributed at least.</p>
<h2 id="heading-future-game-jams">Future Game Jams</h2>
<p>I see myself running through many game jams in the future. Most will be about testing my (currently developing) engine, as well as trying to see what fits with "team organizing". Planning itself is an important thing, so hopefully in the future I can get more of that checked, so more time is spent efficiently instead of rushing and worrying.</p>
<h1 id="heading-flevar">FlevaR</h1>
<p>You can look out for another blog with more information on project's I've been working on in the past 3 months. As usual, you can join my <a target="_blank" href="https://discord.gg/HR2UsPE">Discord Server</a> or follow me on <a target="_blank" href="https://twitter.com/danidre">Twitter</a> if you want to keep updated with everything or talk development. Until next time, thanks for reading! 😃</p>
]]></content:encoded></item><item><title><![CDATA[React Abominations: Is this Legal?]]></title><description><![CDATA[Hey!
I've been learning React recently, and handling error messages separately in my React Project, and wondered 🤔:

"What if you can merge them together?"

So I tried this:
useErrors.js

It's pretty self-explanatory, but essentially I define a useE...]]></description><link>https://blog.danidre.com/react-abominations</link><guid isPermaLink="true">https://blog.danidre.com/react-abominations</guid><category><![CDATA[React]]></category><category><![CDATA[React]]></category><category><![CDATA[danidre]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Fri, 21 Feb 2020 17:11:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660065435947/v3uapBJx_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-hey">Hey!</h1>
<p>I've been learning <a target="_blank" href="https://reactjs.org/">React</a> recently, and handling error messages separately in my <a target="_blank" href="https://dani-smorum.herokuapp.com/">React Project</a>, and wondered 🤔:</p>
<blockquote>
<p>"What if you can merge them together?"</p>
</blockquote>
<p>So I tried this:</p>
<h2 id="heading-useerrorsjs">useErrors.js</h2>
<p><img src="https://i.imgur.com/TAQ0Wlr.jpg" alt="Code 1" class="image--center mx-auto" /></p>
<p>It's pretty self-explanatory, but essentially I define a <code>useErrors</code> function that creates an <code>error</code> state. Then, within that same function, I create an <code>Error</code> Component, which checks if an error exists. If no error exists, it is set to false; any other value would mean that there is an error.</p>
<p>Thus, the <code>Error</code> Component renders accordingly, and the <code>useErrors</code> function returns that <code>Error</code> Component, along with the <code>setError</code> function.</p>
<hr />
<h1 id="heading-usage">Usage:</h1>
<h2 id="heading-testjs">test.js</h2>
<p><img src="https://i.imgur.com/Iz4Ns7G.jpg" alt="Code 2" class="image--center mx-auto" /></p>
<p>The <code>useErrors</code> function is handled 4 ways:</p>
<ul>
<li><code>import useErrors from "./useErrors";</code> - imports the function.</li>
<li><code>const [Error, setError] = useErrors(false);</code> - creates the Error state, passing in the default error value (a value of false is automatically hidden), and returns the <code>Error</code> Component, as well as the <code>setError</code> function.</li>
<li><code>setError([&lt;value&gt; | false);</code> - invokes the function to set the error message. A value of false is hidden; any other value is displayed as the error message.</li>
<li><code>&lt;Error /&gt;</code> - the JSX to render the error message. (Notice no props are sent, yet it works?)</li>
</ul>
<p>When running the code, the error is displayed:</p>
<p><img src="https://i.imgur.com/6Td3t2k.jpg" alt="Error 1 Pic" class="image--center mx-auto" /></p>
<p>And, when ready, it can be closed (which just sets the <code>error</code> state to false)</p>
<hr />
<h1 id="heading-more-testing">More Testing:</h1>
<p>To further test, we can create a loop that sets random error messages and clears them:</p>
<h2 id="heading-testjs">test.js</h2>
<p><img src="https://i.imgur.com/aEg0uFS.jpg" alt="Code 3" class="image--center mx-auto" /></p>
<p><img src="https://i.imgur.com/mZ7MTxt.gif" alt="Error 2 Gif" class="image--center mx-auto" /></p>
<p>It even works when you create multiple <code>Error</code> States (different names for different messages), and you could reuse the same <code>Error</code> Component multiple times on the page and it'll <strong>React</strong> the same for that <code>setError</code>:</p>
<p><img src="https://i.imgur.com/BmrO3b8.gif" alt="Error 3 Gif" class="image--center mx-auto" /></p>
<h1 id="heading-but-how-why">But... how? Why?</h1>
<p>From my understanding, <code>React Components</code> are reusable, either if they're functional or class-based. And the usual way data is sent/updated per Component is either through <code>state</code> or <code>props</code>. So how does a variable from a parent function run down into that Component, while being unique to that one instance of error message?</p>
<ul>
<li>Is any data leak happening?</li>
<li>Are there resources out there that can educate me on what I'm currently doing?</li>
<li>Is this bad practice? I lack knowledge of the terminology for such a thing, all I know is...<strong>it works</strong></li>
</ul>
<hr />
<p>Until I've successfully created commenting on posts to this website, you can find me on Twitter <a target="_blank" href="https://twitter.com/danidre">@danidre</a> or join my <a target="_blank" href="https://discord.gg/HR2UsPE">Discord</a> server. Feel free to message me on the <a target="_blank" href="https://discord.gg/6CFNmP7">Nodeiflux</a> server and share your thoughts!</p>
<h1 id="heading-its-possible-its-wrongbut-it-works">It's possible it's wrong...but it works! 😉</h1>
]]></content:encoded></item><item><title><![CDATA[Plans For 2020]]></title><description><![CDATA[2019 has come and gone, and many sudden changes occurred:

Graduated from high school
Learnt and created my new website
Participated in my first Ludum Dare Jam in years!
Successfully moved away from ActionScript2.0 (Flash) to Javascript.



More deve...]]></description><link>https://blog.danidre.com/plans-for-2020</link><guid isPermaLink="true">https://blog.danidre.com/plans-for-2020</guid><category><![CDATA[planning]]></category><category><![CDATA[danidre]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Wed, 01 Jan 2020 18:00:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659982105696/7nzMVLcYg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.discordapp.com/attachments/234473699460382721/661948884137934864/unknown.png" alt="Happy New Year image" class="image--center mx-auto" /></p>
<h2 id="heading-2019-has-come-and-gone-and-many-sudden-changes-occurred">2019 has come and gone, and many sudden changes occurred:</h2>
<ul>
<li>Graduated from high school</li>
<li>Learnt <em>and created</em> my new website</li>
<li>Participated in my first <a target="_blank" href="https://ldjam.com/events/ludum-dare/45/bob-the-brawler">Ludum Dare Jam</a> in years!</li>
<li>Successfully moved away from ActionScript2.0 (Flash) to Javascript.</li>
</ul>
<hr />
<p><img src="https://cdn.discordapp.com/attachments/234473699460382721/661949274572980265/unknown.png" alt="Resolutions and Plans image" class="image--center mx-auto" /></p>
<h1 id="heading-more-development-on-btbio">More development on BtBIO</h1>
<p>I took November to learn about multiplayer, and worked on <a target="_blank" href="https://btbiopbe.herokuapp.com/">this project</a>. A multiplayer version of my Ludum Dare game, using SocketIO and NodeJS to work on browsers. I plan to continue working on this game until I've carried everything in the singleplayer game across.</p>
<h1 id="heading-development-and-collaborations-on-other-projects">Development and collaborations on other projects</h1>
<p>I'm interesting in participating in more game jams, as well as getting more experience in collaborating with other developers on other games.</p>
<p>Additionally, I plan to learn <strong>React</strong> and create more applications or websites, as well as continue building/adding features to this website (user accounts, comments, user messaging, etc).</p>
<h1 id="heading-job-employment">Job employment</h1>
<p>Since I've recently graduated, I need to get working experience. Ideally, I'd like to be employed at a <em>tech</em> company that also works on games or websites; but for now, anything that could give experience.</p>
<h1 id="heading-artwork">Artwork</h1>
<p>I'm no artist, but I do find joy in doodling here and there.</p>
<p><img src="http://www.hyunsdojo.com/uploads/photos/0fb6f46224302986_cec0c810_f.png" alt="eye image" class="image--center mx-auto" /></p>
<p>I do hope I can work on these more and possibly add a doodles section to this website as well!</p>
<hr />
<p><img src="https://cdn.discordapp.com/attachments/234473699460382721/661950257717837826/unknown.png" alt="Final 2020 Plans Image" class="image--center mx-auto" /></p>
<p>Moving forward into 2020, there're lots of things I'm excited to do; some with higher priorities.</p>
<p>Game-making would be the most challenging change, as I hope I can try a new genre (so far, it's always been collision-based games). I'm not sure what new games I want to make yet, but the idea of non-collision remains. And...maybe earn a few bucks in the making? 😂</p>
<p>Follow me on Twitter <a target="_blank" href="https://twitter.com/danidre">@danidre</a> or join my <a target="_blank" href="https://discord.gg/HR2UsPE">Discord Server</a> to keep up to date with any changes. But until next time...</p>
<h2 id="heading-stay-tuned"><em>Stay tuned</em></h2>
]]></content:encoded></item><item><title><![CDATA[Ludum Dare 45 Results]]></title><description><![CDATA[3 Weeks Ago
I participated in my first collab Ludum Dare Jam; now results are out. It's bittersweet.  

Overall: 541st (3.473 average from 114 ratings)  
Fun: 417th (3.438 average from 114 ratings)  
Innovation: 861st (2.784 average from 113 ratings)...]]></description><link>https://blog.danidre.com/ludum-dare-45-results</link><guid isPermaLink="true">https://blog.danidre.com/ludum-dare-45-results</guid><category><![CDATA[ludumdare45]]></category><category><![CDATA[Game Jam]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[danidre]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Wed, 30 Oct 2019 17:32:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659981128768/vPjTNjwiG.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-3-weeks-ago">3 Weeks Ago</h2>
<p>I participated in my first collab Ludum Dare Jam; now results are out. It's bittersweet.  </p>
<ul>
<li>Overall: <strong>541st</strong> (3.473 average from 114 ratings)  </li>
<li>Fun: <strong>417th</strong> (3.438 average from 114 ratings)  </li>
<li>Innovation: <strong>861st</strong> (2.784 average from 113 ratings)  </li>
<li>Theme: <strong>1052nd</strong> (2.383 average from 113 ratings)  </li>
<li>Graphics: <strong>380th</strong> (3.795 average from 114 ratings)  </li>
<li>Audio: <strong>334th</strong> (3.444 average from 110 ratings)  </li>
<li>Humor: <strong>413th</strong> (3 average from 106 ratings)  </li>
<li>Mood: <strong>650th</strong> (3.241 average from 108 ratings)  </li>
</ul>
<blockquote>
<p>wuuuut, but I had so many ratings, and good karma~  </p>
</blockquote>
<p><img src="https://static.jam.vg/raw/0fb/8/z/29ee1.jpg" alt="btb_ratings.JPG" class="image--center mx-auto" />  </p>
<h2 id="heading-quality-over-quantity-gets-the-cake">Quality over quantity, gets the cake!</h2>
<p>Not like that's a bad thing, that's how it <em>should be</em>!  </p>
<blockquote>
<p>so then why was 9/10 of the reviews "excellent game, amazing graphics, audio fit the theme, fun gameplay~"  </p>
</blockquote>
<h3 id="heading-deceptive-much">Deceptive much?</h3>
<p>Well, I guess people did want their games played. And don't get me wrong, I enjoyed most of the games I played 🤣</p>
<p>So I guess my game was just bad?</p>
<p>The trend in the <strong>top 100</strong>, unless your game is a globally popular one, once 20-40 people voted on your game and they're all your audience, that'll sure trump 100+ of varying audiences with different tastes for varying genres.  </p>
<blockquote>
<p>but saying that is making an excuse, right?   </p>
</blockquote>
<h3 id="heading-no-copping-out-do-better-next-time">No copping out, do better next time.</h3>
<p>It goes back to the saying, if my game was better, the results would have shown that. Because there are the rare <strong>top 100</strong> that have 150+ votes. And unless mine was on that caliber of awesomeness, I shouldn't expect such glorious results 😂</p>
<blockquote>
<p>but what about the other amazing games that didn't make it?   </p>
</blockquote>
<h2 id="heading-tactics-skills">Tactics! Skills!</h2>
<p><strong><a target="_blank" href="https://ldjam.com/events/ludum-dare/45/bloodless">Bloodless</a></strong> was the top jam game during votings; it placed <strong>97th</strong> overall. Other tops, weren't as lucky. Many of these results surprise me, as I genuinely thought hands down, that they'd make it.</p>
<p>As for the <strong>top 100</strong> games; they did so well to keep their ratings just above the Danger Zone <em>(games ordered by 20 votes or lower)</em>, and their personal reviews below the Smart Zone <em>(games ordered based on how many reviews their author made)</em>. Because I could not find them 😅</p>
<blockquote>
<p>was that their master plan???   </p>
</blockquote>
<h3 id="heading-well-im-new-to-all-this-but-it-doesnt-change-my-personal-experiences-earned">Well, I'm new to all this, but it doesn't change my personal experiences earned.</h3>
<h1 id="heading-lessons-learnt">Lessons learnt:</h1>
<ul>
<li><strong>Maybe if:</strong> I turned it into a puzzle game, it'd have done better. Many people liked the idea of a puzzle brawler. <strong>But</strong> I barely got time to complete the Brawler, who'd have time to delve into a new category of gaming like puzzles, <em>especially as this Brawler was a new game genre for me as well!</em>  </li>
<li><strong>Maybe if:</strong> I aimed for only the minimum ratings <em>(20)</em>, results would have generally been higher numbers? <strong>But</strong> these extensive ratings are what pushed me to <a target="_blank" href="https://trello.com/b/HgDlN6cd/bob-the-brawler-20">take this game further!</a>  </li>
<li><strong>Maybe if:</strong> I voted minimally, I too would avoid my game appearing much. <strong>But</strong> who wouldn't want to play all these games! Besides, what's the point of Ludum Dare if you just make a game, drop it in, and move on!? <em>(the main objective is to start something but that alone is boring; sharing/interacting is fun!)</em>  </li>
<li><strong>Maybe if:</strong> I listened to the audience and changed the controls, it'd have been less spam-feely. <strong>But</strong> 3/4 of the players seemed to love it! Besides, this is what I envisioned for this game! Regardless of if it was well received or not.</li>
<li><strong>Maybe if:</strong> I made a better game, results would have been better. <strong>But</strong> it's possible the system is flawed; <strong>or not</strong>. It is what it is.  </li>
</ul>
<h2 id="heading-overall">Overall:</h2>
<ul>
<li>It was amazing making a game for the first time with an artist. <a target="_blank" href="https://danidre.herokuapp.com/u/AXLplosion/">@AXLplosion</a> was able to take all my ideas and bring them to life! I'm just sad I couldn't make the game better for his sake.  </li>
<li>I'm satisfied with the progress I made, utilizing game libraries and APIs I started working on in preparation for the event; call them my own "engine".</li>
<li>I'm amazed by the game I accomplished, and I'm excited to continue building on it <em>did someone say multiplayer 👀</em>; I did not know what I was getting into when I decided to make a Brawler, and I must say, the spaghetti code was worth it!</li>
<li>I'm grateful to Ludum Dare for the initiative they make to prompt a game, it really pushed my limits and helped me grow; looking forward for more competitions.</li>
<li>I'm just disappointed with these results.  </li>
</ul>
<h2 id="heading-but-hey-at-least-bob-the-brawlerhttpswwwnewgroundscomportalviewith740364-is-frontpaged-on-newgroundshttpswwwnewgroundscom-at-this-time-of-writing">But hey, at least <a target="_blank" href="https://www.newgrounds.com/portal/view/740364">Bob the Brawler</a> is Frontpaged on <a target="_blank" href="https://www.newgrounds.com/">Newgrounds</a> <em>(at this time of writing)</em></h2>
<p>Seeing it climb from Daily Pick to Popular Game to Frontpaged was an excitement of it's own 😆</p>
<p><img src="https://static.jam.vg/raw/0fb/8/z/29ed5.jpg" alt="btb_dailyfeeature.JPG" class="image--center mx-auto" />
<img src="https://static.jam.vg/raw/0fb/8/z/29ed6.jpg" alt="Capture62.JPG" class="image--center mx-auto" />
<img src="https://static.jam.vg/raw/0fb/8/z/29ed7.jpg" alt="btb_frontpaged.JPG" class="image--center mx-auto" /></p>
<h2 id="heading-moving-on">Moving on:</h2>
<p>Bob the Brawler is my Ludum Dare entry. But it did spark some ideas I'm willing to expand on, as well was include suggestions from all the reviews on the game.  </p>
<p><img src="https://static.jam.vg/raw/0fb/8/z/29ed9.jpg" alt="btb_trello.JPG" class="image--center mx-auto" /></p>
<p>In addition to that, I'm also dabbling in some <a target="_blank" href="https://multiplayergaemtest.herokuapp.com/">multiplayer experiments</a>, developing my own systems for <a target="_blank" href="https://gabrielgambetta.com/client-side-prediction-live-demo.html">Client-Side Prediction and Server Reconciliation, Entity Interpolation, and Lag Compensation</a>, based on <a target="_blank" href="https://gabrielgambetta.com/client-server-game-architecture.html">these articles</a> from <a target="_blank" href="https://gabrielgambetta.com/index.html">Gabriel Gambetta</a>.  </p>
<p><img src="https://static.jam.vg/raw/0fb/8/z/29edb.gif" alt="multiplayergeamtest.gif" class="image--center mx-auto" /> </p>
<blockquote>
<p>get a friend or another window, and just move around in this canvas. it's just a test to see how websockets work, so tell me if it lags: <a target="_blank" href="https://multiplayergaemtest.herokuapp.com/">multiplayergaemtest</a>   </p>
</blockquote>
<h3 id="heading-but-as-this-event-is-up-and-over-im-going-silent-for-a-while-congratulations-to-the-winners">But as this event is up and over, I'm going silent for a while. Congratulations to the winners!</h3>
<h1 id="heading-see-you-next-year-april">See you next year April!</h1>
<hr />
<h3 id="heading-follow-me-on-twitterhttpstwittercomdanidre-or-join-my-discord-serverhttpsdiscordgghr2uspe-to-keep-up-to-date-on-any-activity-from-me">Follow me on <a target="_blank" href="https://twitter.com/danidre">Twitter</a> or join my <a target="_blank" href="https://discord.gg/HR2UsPE">Discord Server</a> to keep up-to-date on any activity from me.</h3>
]]></content:encoded></item><item><title><![CDATA[Ludum Dare 45]]></title><description><![CDATA[Hey guys!
It’s been 4 years since I’ve last entered a game jam! Ludum Dare 29 - 34 had game after game from me.

Then school took away my time…. 😔
Until now! Highschool’s finally over! 😄


I’ve spent the July/August holidays learning, and have made...]]></description><link>https://blog.danidre.com/ludum-dare-45</link><guid isPermaLink="true">https://blog.danidre.com/ludum-dare-45</guid><category><![CDATA[Game Jam]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[ludumdare45]]></category><category><![CDATA[danidre]]></category><dc:creator><![CDATA[Carlonn Rivers]]></dc:creator><pubDate>Wed, 02 Oct 2019 17:06:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659976912439/2SGpol4Zg.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-hey-guys">Hey guys!</h1>
<p>It’s been 4 years since I’ve last entered a game jam! <a target="_blank" href="http://ludumdare.com/compo/author/danidre14/">Ludum Dare 29 - 34</a> had game after game from me.</p>
<blockquote>
<p>Then school took away my time…. 😔</p>
<p>Until now! Highschool’s finally over! 😄</p>
</blockquote>
<hr />
<p>I’ve spent the July/August holidays learning, and have made <a target="_blank" href="https://danidre.com"><strong>this</strong></a> website. A great improvement from the <a target="_blank" href="https://danidre14.wixsite.com/danidre14">click and drag website</a> I made back when I was 14.</p>
<p>Thus, I finally have somewhere to host all the multiplayer games I hope and dream of making. 😛</p>
<p>But, there’s only one problem… <strong>flash is dying!</strong> 😪</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=DuNYJ0KZWhk">https://www.youtube.com/watch?v=DuNYJ0KZWhk</a></div>
<p>Inevitably, I soon learned and adopted Javascript and HTML5 canvas, as it was very similar to ActionScript2.0; so I began working on my own <em>game engine</em>, which includes:</p>
<ul>
<li>my fully functioning and resizable/scaleable canvas panel</li>
<li>my own audio system</li>
<li>my own Highscore API (for users with accounts on my website)</li>
</ul>
<h1 id="heading-soludum-dare">So...Ludum Dare?</h1>
<p>Yes! I’m excited and ready to finally participate in Ludum Dare once again! <strong>I’m in!</strong></p>
<p>Additionally, I'll be teaming up with <a target="_blank" href="https://twitter.com/AXLplosion">AXLplosion</a>! He will be creating all the art for the game. 😃</p>
<h1 id="heading-tools-tools-tools">tools tools TOOLS:</h1>
<ul>
<li><strong>Software/Engine:</strong> Pure Javascript + HTML5 Canvas (I want to learn another engine but haven’t gotten there yet. Maybe Unity? Haxe?)</li>
<li><strong>Sprites/Art:</strong> <a target="_blank" href="https://danidre.com/u/AXLplosion/">@AXLplosion</a></li>
<li><strong>Sound effects:</strong> SFXR/BFXR/JFXR: (Very, very, basic sound effects)</li>
<li><strong>Game Music:</strong> LMMS/FL Studio/Bosca Ceiol/PixiTrack:
I’m probably going to download free music and sound effects (if I use music at all) 🤣</li>
</ul>
<h1 id="heading-streaming">Streaming?</h1>
<p>I don't know the outcome and what to expect, but I plan on streaming the process.</p>
<p>Be sure to follow me on <a target="_blank" href="https://www.twitch.tv/danidre14">Twitch</a> and turn on notifications so you get alerted when I go live.</p>
]]></content:encoded></item></channel></rss>