Description

This is just a small game I wrote to both practice JavaScript/HTML5, as well as to poke a little fun at some technical analysis studies 🙂.

In this game, the stock price is controlled by a random variable (i.e. any profit/loss is based on luck). I initially intended to skew the randomness slightly toward the upside so that the best strategy would be to buy and hold, but I figured the game wouldn't be fun anymore if that were the case.

The TA studies are calculated based on their real formulae (though on a shortened timeframe), but essentially their only purpose in the game is to make the chart cluttered to the point of being unreadable. I do recognize that in the real world, there are a variety of factors that influence the market in a non-random fashion, but I think it is important to remember that ultimately, all studies are just predictions which may or may not pan out, and that we should take buy/sell signals with a large grain of salt when making trading decisions.

As of now, the game is not finished yet (there's still quite a few more studies I have to add), but since it was my first time writing a game, there were some learning points that I wanted to write down before I forget.

Lessons Learned

> Concurrent Animations

The Problem

One of the biggest challenges I ran into while making this game was the problem of getting multiple animations going at the same time. I used the old setTimeout() method to create this game (I mainly followed this tutorial), so essentially the animation is done via one main draw function which is called over and over again.

This method worked fine when I created the initial animation for the stock price, but given that this is a game, surely the user needs visual feedback when they buy and sell the stock! What I needed, then, was a separate draw function which executes on click.

I began working on this new function when I came to the unfortunate realization that whatever it drew to the canvas would immediately be erased the next time the main draw function was called. Somehow, I needed to include the buy animation within the main draw function. This actually ended up having a fairly simple solution. I'm not sure if it's the most efficient, but I hadn't seen it on StackOverflow when I searched, so I wanted to write about it here.

The Solution

We create a global variable funArray , which is essentially just an array containing all the custom draw functions we want to add. In our main draw function, we then loop through this array and execute every function in it. Therefore, all we have to do when the user clicks on the screen is push the "buy" or "sell" draw function to this array!

Hmm, but this doesn't seem good enough--we don't just want to draw something static, we want to create an animation. This means that our custom draw functions need to draw something slightly different each time they're called!

One solution could be to code multiple frames for the function to draw, and increment some sort of global variable which represents the frame number. However, this probably isn't a very flexible solution--if we wanted to have three custom draw functions, we'd have to implement three separate variables to track their current frame.

Instead, what we can do is make each of the functions have their own local frame-counting variable! This makes it more convenient, as we can have as many of these custom draw functions as we like without the added hassle of adding a new global variable. Here is the implementation (inspired by the implementation of memoization, although the application here is quite different).

function frameTracker() { var frameCount = 0; // set a local frame-counting variable function draw(instructions) { if (frameCount < instructions.length) { instructions[frameCount](); // draw the current frame frameCount++; // increment the counter } else { // if we've reached our last frame... instructions.slice(-1)[0](); // keep drawing the last frame } } return draw; }

In the code above, "instructions" is an array in which every element is a function which draws a frame of the animation (for an example, have a look at generateBuyInstructions() in index.js ). Having an array full of functions also has the added benefit of being able to take parameters, thereby allowing us to create animations based on the game state.

Using the frameTracker() function, this is how I implemented buy() , which is called when the user clicks on the screen (some variables and game-state updates omitted for simplicity):

function buy() { var instructions = generateBuyInstructions(); // frames for the "buy" animation var animate = frameTracker(); // create a frame-tracking instance funArray.push(() => {animate(instructions)}); // push to global funArray }