Understanding requestAnimationFrame


I never quite got what was the requestAnimationFrame function I kept seeing while reading web articles since I never really dove deep in graphics in the browser.

Still, I think it will be a good exercise and reminder for the future to write a small article to structure and understand the basic concept and usage of the function.


For the human brain to be fooled into thinking that a sequence of images flow into movement (animate), you need to provide at least 24 images (frames) per second, otherwise you'll start to notice cuts. Most computer screens have an "image refresh" (or frames/per/second) rate of 60fps, meaning in turn that you will see 60 images in the course of one second.

If we divide the slice of time for perception (a second) into the refresh rate of the screen it gives us the time slot available to perform an animation (both calculating and painting) for a single frame. We use miliseconds since setTimeout, setInterval use them to measure time.

1000ms / 60 fps = 16.67ms/frame

The smoothness of an animation depends on whether each frame of the animation is executed within that 16.67ms time frame.

An example animation taken from here but slightly modified includes a green square being moved horizontally from left to right using the setInterval function (Demo here).

<div id="animated"><div>
#animated {
    position: absolute;
    width: 20px;
    height: 20px;
    background: green;
let elem = document.getElementById("animated"),
  left = 0,

function animate() {
  elem.style.left = ( left += 10 ) + "px";
  if ( left == 1000 ) {

timer = setInterval(function() {
}, 17);

As seen, I've rounded the 16.67ms to 17ms, and you can see the continuity and somewhat fluidity of the animation. What will happen if we change the interval timer to say, 100ms, is that animation will feel choppy and stumbling. Additionally, there is no real guarantee that the setInterval function will comply with the passed timeout.

What is requestAnimationFrame()

It is a javascript API function that allows the user to perform animation-based loops more efficiently than the usual alternatives: for-loop or setTimeout & setInterval functions.

Intensive graphics animations run with requestAnimationFrame API will get some preferent treatment and be optimized to create a smoother feel in the browser, compared to the previously mentioned counterparts:

  • Animations will only run when visible: the browser will drastically throttle animation if it isn't executed in the active tab, hence saving up a ton of cpu cycles.
  • Browser with setTimeout will update the screen arbitrarily, trying to match the animation's redraw with the whole screen repaint, losing cpu cycles in the process.
  • It does not require an update interval, hence adapting to the refresh rate of the computer. Animations are refreshed when possible, not when told.
  • It will group all the animations into a single repaint, saving a lot of cpu cycles too.
  • Battery friendly: as a consequence of the previous reasons.

Animations can be of any kind, either by manipulating the DOM, using WebGL, using CSS transitions, or using the canvas HTML element.

How does it work

Use of the requestAnimationFrame API is very simple:

  • It accepts a callback function that will be called before triggering a repaint. The callback will receive the timestamp when the API was called.
  • It returns an integer representing the frame id that can be passed to the cancelAnimationFrame API to stop it from being painted.

The former example transformed to use requestAnimationFrame can be found in this pen:

let elem = document.getElementById("animated");

function animate(left) {
  return requestAnimationFrame((timestamp) => {
    elem.style.left = (left + 10) + "px";
    if (left < 1000) 
      return animate(left+10);


Instead of using a pure function it can also be done by tracking the left variable externally, as seen in this pen:

let elem = document.getElementById("animated"),
    left = 0;

function animate(timestamp) {
  elem.style.left = (left += 10) + "px";
  if (left < 1000)
    return requestAnimationFrame(animate);


Cancelling requestAnimationFrame() execution

As stated previously, animations can be cancelled by making a call to the cancelAnimationFrame API and passing the frame id returned by requestAnimationFrame. Check this pen to see the animation cancelled after one second. Then, comment out the setTimeout line and see how the green square moves way forward.

let elem = document.getElementById("animated"),
    left = 0,

function animate(timestamp) {
  elem.style.left = (left += 10) + "px";
  if (left < 1000)
    frameId = requestAnimationFrame(animate);

frameId = requestAnimationFrame(animate);

setTimeout(() => cancelAnimationFrame(frameId), 1000);

Slowing down the animation

The frame rate of an animation using requestAnimationFrame can also be adjusted by throttling the call to the API inside a setTimeout call and dividing a second between the frame rate desired, slowering it down, like shown in this pen.

let elem = document.getElementById("animated"),
    left = 0,
    fps = 10;

function animate(timestamp) {
  setTimeout(() => {
    elem.style.left = (left += 10) + "px";
    if (left < 1000)
  }, 1000/fps);


So, that is pretty much it. requestAnimationFrame is a basic construct used in modern javascript frameworks and libraries such as React, Vue, Preact, Phaser, Pixi, etc..

Have fun!


  1. https://www.nczonline.net/blog/2011/05/03/better-javascript-animations-with-requestanimationframe/
  2. https://dev.opera.com/articles/better-performance-with-requestanimationframe/
  3. http://blog.teamtreehouse.com/efficient-animations-with-requestanimationframe
  4. https://www.html5rocks.com/en/tutorials/speed/rendering/
  5. https://hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
  6. http://www.javascriptkit.com/javatutors/requestanimationframe.shtml
  7. http://creativejs.com/resources/requestanimationframe/index.html

A primer on Pretty Good Privacy


I've had the intention of writing an article about PGP for a long time, but every time I started reading about the topic I almost immediately brushed the idea off. I believe this is spurred by the fact that understanding it is not quite simple.

Just as monads, once you get the hang of it it becomes evident and you automatically lose the ability to communicate the concept to others.

Therefore, finally I decided to wrap my head around the matter and write a small article to try to simplify this concepts mostly for myself and hopefully for whomever reads it.

What is PGP?

The simplest way I think of it is as an information's encryption technology. By information I mean as stated by the wikipedia article: emails, files, text, directories and whole disk partitions.

It provides a framework to exchange data securely among peers, as well as guaranteeing both the authenticity and integrity of the message.

How does it work?

There are three main concepts I split PGP into:

  1. Encryption/Decryption
  2. Authenticity & Integrity
  3. Web of Trust

1. Encryption/Decryption

Encrypting a message is the act of codifying it to make it unreadable to an external observer other than the intended recipient. The encoded message is called ciphertext. Decrypting is the inverse process where an unreadable message becomes accessible (converting from ciphertext back to plain text).


PGP uses several techniques to achieve that goal: hashing, data compression and symmetric/asymmetric cryptography. Explaining each one of them separately would go beyond the scope of the article. Moreover, they are no simple concepts to fathom, there is quite a bit of literature about them.

The process of data encryption & decryption has multiple steps to be taken into account. The best way to understand it is with a simple diagram where the flow of information being encrypted and sent from a Sender to a Receiver is depicted.


Compression: initially the message is compressed. This is done with a multiple purpose in mind: to reduce the amount of bytes to be transmitted, thus increasing performance, and additionally to enhance the cryptographic strength. Messages can have patterns that can be spotted in the text after encryption; compression helps on preventing those patterns to leak.

A new Session Key is generated per each one of the messages sent; this is a random string created by using multiple sources of entropy (randomness) in your computer, for example mouse movements. This key is then used to encrypt the compressed message data using symmetric cryptography, creating a compressed ciphertext.

Afterwards, the Session Key alone is encrypted with the Receiver's public key (asymmetric cryptography) and attached to the compressed ciphertext, generating the whole encrypted message. The Session Key is later discarded.

If the whole message is encrypted using the Receiver's public key (asymmetric), the size of the resulting message would spike, as well as dramatically increasing the computation time. As instead, by only encrypting the session key the process is faster and the final payload way smaller.

Inversely, what is left for the receiver is to use their private key to decrypt the session key, and use that session key to decrypt the entire message.


2. Authenticity & Integrity

PGP also provides capabilities to verify whether the received message has been sent by a specific sender. To give an example, how do you know that an email you received from personitrust@trust.com is actually from that person and not some impostor?

The solution PGP provides is by creating a digital signature of the message and sending it along so the receiver can verify it's origin. The process overview is like this:


Then, the Receiver will use the Sender's public key to verify the integrity of the message's hash that was signed with the Sender's private key.

Because there is a unique association between public and private key, if the sender uses a certain private key to sign a message and you verify the signature using the corresponding public, then the signature verification will succeed only if the message has not been altered.

There might be however some cases where such statement does not hold (See more).

3. Web of Trust

Public keys can be obtained in multiple manners: by downloading them from the site of the interested party, by email, using an untrusted key server, etc.. PGP stores all the public keys we need to communicate in a file called the keyring.

You can sign several public keys, as so can others, establishing a network of trust among public key holders where for example you may accept a document signed by some external party whose public key has been signed enough times from several other sources you've deposited your confidence in.


Sending an encrypted message using GPG

GPG is the free implementation for PGP, while the original one is protected. Lets think of the simple scenario where a Sender wants to send a "Hello Amigo!" message to a Receiver.

  1. First, generate the key pair of the Receiver.
λ gpg --gen-key

This process will ask us for some information:

  • Kind of key: 1 (RSA by default)
  • Keysize: 4096
  • name: Receiver
  • email: receiver@receiver.com
  • password (to encrypt the private key symmetrically): receiver
  1. Check the key pair was properly created.

For public key

λ gpg --list-keys
pub   4096R/D725A05G 2018-05-09
uid                  Receiver <receiver@receiver.com>
sub   4096R/D69D7E5G 2018-05-09

For private key

λ gpg --list-secret-keys
sec   4096R/D725A05G 2018-05-09
uid                  Receiver <receiver@receiver.com>
ssb   4096R/D69D7E5G 2018-05-09

Then we encrypt the Hello Amigo! sentence using the Receiver's public key (--armor option tells GPG to generate a ascii-armored kind of message).

λ echo "Hello Amigo!" | gpg --encrypt --armor -r D725A05G > encrypted.msg
λ cat encrypted.msg


Now we ideally we send this encrypted.msg gibberish text to the Receiver who is in posession of their private key, and they just need to type the key's password he established when they created it (receiver).

λ cat encrypted.msg | gpg

You need a passphrase to unlock the secret key for
user: "Receiver <receiver@receiver.com>"
4096-bit RSA key, ID D69D7E5G, created 2018-05-09 (main key ID D725A05G)

gpg: encrypted with 4096-bit RSA key, ID D69D7E5G, created 2018-05-09
      "Receiver <receiver@receiver.com>"
Hello Amigo!

That's it, Have fun!


  1. https://users.ece.cmu.edu/~adrian/630-f04/PGP-intro.html
  2. https://en.wikipedia.org/wiki/Pretty_Good_Privacy
  3. https://en.wikipedia.org/wiki/GNU_Privacy_Guard
  4. https://security.stackexchange.com/questions/82490/when-signing-email-with-gpg-how-does-verification-by-the-receiver-work
  5. http://zacharyvoase.com/2009/08/20/openpgp/
  6. https://www.cs.bham.ac.uk/~mdr/teaching/modules/security/lectures/PGP.html
  7. https://www.openpgp.org/software/
  8. https://gnupg.org/

Sabbatical two months update


Hello! It has been a long time since I last updated. I left my job and Belgium two months ago at the end of February to give myself some perspective and travel a bit.

I've kept myself busy ever since. I wanted to create a small update to gather all the things that have attracted my interest since I stopped my daily job. Additionally, I find useful to enumerate some of the things I have in my backlog and I hope to tackle as soon as I have time.

Unfortunately I have been quite disconnected to technical writing and messing around with software projects, but I will try to go back as soon as I can.

Things I've done

  • I fixed my bike at home.
  • I started with the A2 motorcycle permit, and passed already the first test.
  • I started learning lockpicking and I managed to open two locks so far!
  • I wrote a quick & dirty scraper in python to automate some downloads.
  • I started learning arabic with Mondly, but I have stopped with it for now.
  • I have started reading an investment book to learn the basics.
  • I learnt Wordpress and I created & sketched what is going to be my alternate site, mostly oriented towards travel articles and reflections: https://thedrift.io. I will try to reflect my musings and experiences over this sabbatical time there.
  • I booked a flight to Singapore. I will start my trip from there.
  • I did a job coaching online course to try to figure out my next steps.

Things I want to do

  • I want to become proficient in Emacs, but this looks like a lifelong task.
  • I want to dive deep in a functional language. I keep doubting between Clojure, Common Lisp and Scala.
  • I want to learn more about Web Assembly.
  • I want to learn more about the blockchain.
  • I want to do some challenges in Hackattic.
  • And of course, keep up with the things I have already started with.

This is it, hopefully when I start traveling I will be more prolific with my articles.

Have fun!

Year 2017 review and resolutions

Every time a new year passes by I feel a mix of two reactions: on one side, my first impression is always of a slight disappointment, as if I had not accomplished anything worth mentioning or hadn't made much more of a change compared to how I was when the year first started.

Then, I try to keep a cold mind and start enumerating the nice, interesting things I have done and learned, as well as all the new friendships made and the strengthening of existing ones, and slowly I tear down the thin depression web I made myself lay upon me.


I don't really count Belgium since this is my third year living here, and 2018 will start as my fourth year outside my beloved Spain.

I have been to quite some nice places though. Some of them I had already been there, some of them were new, bust mostly I prioritized people over places themselves. Almost every new city or country I went to was due to the interest of catching up with old friends or traveling together for a long weekend. Theres is however one long trip exception. I don't have the places noted down so I'll try to draw them directly from memory.

  • Germany: Berlin.
  • The Netherlands: Amsterdam, Den Haag, Maastricht, Rotterdam.
  • Spain: Barcelona, Madrid.
  • United Kingdom: London.
  • Norway: Bergen.
  • France: Paris, Reunion Island.
  • Bulgaria: Sunny Beach.

Most of the places I went to were due to friends living there, visiting over, or just an excuse to make a small trip. Reunion Island was the best trip I have ever done in my life. The island is a Département d'Outre Mer belonging to France, a small island in the Indic Ocean, next to Madagascar and Mauritius, is a paradise on earth, a territory full of incredible mountains, beautiful views, amazing food and unbelievably welcoming people.

A hodgepodge of cultures, religions and origins. A hidden oasis for those who seek for an inner voyage.


Best. Place. Ever.

Work & Technologies

The year started quite strong, I had moved in October to the Semantic Technology branch of my company at the last part of year 2016 and I had to learn quite fast all the new technologies and stacks that were used in my new team. I won't make an exhaustive list, just some things were I feel I have sensibly improved.

  • Javascript & Ember.js: I still kept strengthening my position as the javascript fan of the company, but as new challenges kept coming, I had to quickly adapt to new scenarios and projects si I had to put those skills a little aside.
  • Python: I developed & maintained several microservices in this language, so I needed to brush up on the it and its particularities. I enjoyed a lot working with async/await, list comprehensions, lambda functions and OOP back again. I am lucky I have a very patient mentor, my colleague @cecton, a pouring fountain of python know-how.
  • Docker: I had to learn, fight and dive into docker containers, images, volumes, docker networking and docker-compose applications, suffering several quirks, since the majority of the projects were developed in this manner.
  • Linux Makefiles: This was equally annoying and fun. I had to extend a data transformation pipeline using a Makefile, so I needed to deal for several weeks with it, it was very nice when I discovered that you can define functions inside Makefiles to avoid repeating code for very similar build targets!
  • SPARQL: Since I work in a Linked Data oriented team, we store data in triplestores accessed with the SPARQL query language, so I needed to learn the basics.
  • Elasticsearch & Kibana: I became familiar with Elasticsearch & Kibana due to a new project that came up and I was the available developer by then. It proved a lucky strike since I had a lot of fun learning them and they are widely used in the industry.

General Learning

  • I had a double improvement: I improved both my presentational skills and my English as the result of doing small presentations and exposing my work to colleagues.
  • I have written quite some blog posts and therefore my technical communication skills feel cleaner.
  • I practiced some Dutch for fun and before I realized I was able to maintain simple but complete conversations while doing my daily tasks.
  • I learned how to sew very very basicly.
  • I managed to cook some cool dishes when cooking for friends!


I have not done as much sport this year as I would've liked but I've had my fair share of fun.

  • Running: on the beach, in the woods, anywhere.
  • Trampoline park: they opened one in my hometown and had to test it.
  • Gymnastics: this was tough.
  • Climbing: a colleague and I have tested multiple boulder and climbing walls around.
  • Swimming.
  • Crossfit: a single day, very physically taxing, I would think twice before repeating.
  • Working out at the gym: as always, just to stay as fit as possible.

New Year 2018 Resolutions

  • Reading at least 3 technical books.
  • Starting to learn a new language: this is very tricky, since I genuinely enjoy languages and choosing a new one is hard. I want a very complicated one that forces my mind to twist, so I was thinking between chinese, arabic, russian or german.
  • Giving a talk at a conference/meetup: the worst part of this is going to be deciding about the topic..
  • Travel to another continent. Hell, I have never left Europe yet!
  • Learn a new programming language/paradigm. I am thinking in Rust or Clojure.
  • Create a stock market portfolio and perform my first investment.
  • Be able to perform three consecutive muscle-up.

That's it. I think I will be able to do all of them.

Have fun!

mdlink: Linking multiple node modules with a single command


As part of the process of working in node.js applications, developers usually use additional libraries and frameworks to ease and speed their development time, and more than often the number of libraries they rely upon is quite high.

It is also not uncommon to need to modify any of those libraries to fix a bug, or fork them to add additional functionality required for the project they are being used in.

The usual way to do this in a node.js project is:

  1. Clone the library's repository in your local machine
  2. cd your library's folder.
  3. sudo npm link to link the module globally.
  4. cd to your project's folder.
  5. npm link <library_name> to link to the library.

If you have multiple libraries that you need to be linking at the same time, or you have to link a library that depends on another library that you may need to link as well, it can be quite cumbersome to link them one by one.

In my free time I developed mdlink. It is a small utility that allows to easily npm link multiple node modules in a given project.

It was born as a reaction of my annoyance while I was developing Ember.js applications with multiple addons at the same time. Per each addon I'd need to clone it and link it. This tool intends to provide an easy way of bulk npm link and removing those links later while not necessary.

Modules are specified in the mdlink.config.json configuration file. e.g:

  "base_modules_path": "~/gits/test",
  "modules": {
    "mdlink": {
      "url": "https://github.com/fr0gs/mdlink",
      "path": "~/gits/test/mdlink"

Each module to be linked is stated in the modules object. It can have both url & path together, or only one of each.

The base_modules_path is used for the case where there is no path but a url is yet specified. The module will be cloned in base_modules_path/module_name instead.

Let's see an example with a new Ember application. First, download mdlink with npm and create the application:

test-ember:master* λ sudo npm install -g mdlink
test-ember:master* λ ember new test-ember

Then, create a new mdlink.config.json file inside the test-ember app.

test-ember:master* λ mdlink init

And let's say that in our new application we want to locally modify both ember-moment and ember-promise-helpers. We modify the mdlink.config.json file like this:

  "base_modules_path": "/home/esteban/gits/test",
  "modules": {
    "ember-moment": {
      "url": "https://github.com/stefanpenner/ember-moment",
      "path": "/home/esteban/gits/test/ember-moment"
    "ember-promise-helpers": {
      "url": "https://github.com/fivetanley/ember-promise-helpers.git",
      "path": "/home/esteban/gits/test/ember-promise-helpers"

Do the linking:

test-ember:master* λ mdlink s
[+] Path /home/esteban/gits/test-ember/node_modules/ember-moment already exists, removing it.
[+] <path exists> . Successfully cloned https://github.com/stefanpenner/ember-moment in path: /home/esteban/gits/test/ember-moment
[+] Create link from /usr/lib/node_modules/ember-moment -> /home/esteban/gits/test/ember-moment
[+] <path exists> . Successfully cloned https://github.com/fivetanley/ember-promise-helpers.git in path: /home/esteban/gits/test/ember-promise-helpers
[+] Create link from /usr/lib/node_modules/ember-promise-helpers -> /home/esteban/gits/test/ember-promise-helpers

Verify both modules where properly cloned:

test λ ls -la ~/gits/test/
drwxrwxr-x  4 esteban esteban 4,0K Dez 19 22:07 ./
drwxrwxr-x  8 esteban esteban 4,0K Dez 19 22:05 ../
drwxrwxr-x 10 esteban esteban 4,0K Dez 19 22:36 ember-moment/
drwxrwxr-x  9 esteban esteban 4,0K Dez 19 22:07 ember-promise-helpers/

Modify index.js in both modules, and ember s from the test-ember app folder:

test-ember:master* λ ember s
Linked ember-moment
Linked ember-promise-helpers
Could not start watchman
Visit https://ember-cli.com/user-guide/#watchman for more info.
Livereload server on http://localhost:7020

Build successful (14117ms) – Serving on http://localhost:4200/

Have fun!