CssConfEu JsConfEu 2018

← Back to home
Table of contents

Intro

At the beginning of this month I had the privilege to visit CssConfEu and JsConfEu for the first time.

This post doesn’t have a coherent direction and I don’t intend on coming up with one. Still I’d like to write down a few misc notes - so let’s try that!

Arriving at JsConfEu

Experiencing the arch

So Mozilla had a thing in collaboration with Ian Brill called the arch. It was beautiful. Here’s why:

Short video of the arch in colors

📹 Embed Video from:

👁️ Watch on: youtube.com

📋 Copy link to:

📜 Read the Google Privacy Policy

It was a tunnel of color lit cells together with an invitation to program an animation for it. This flyer was distributed in the vicinity:

The arch flyer, front The arch flyer, back

Because I had done something similar before I had a desire to get a particular video playing on the arch.

First I needed to get hold of the material and get it into shape. So I dug out a few console commands:

# Download video:
youtube-dl "https://www.youtube.com/watch?v=oHg5SJYRHA0"
# Resize video to desired resolution:
ffmpeg -i RickR…YRHA0.mp4 -vf scale=44:36 smallRolled.mp4
# Cut video to desired length:
ffmpeg -i smallRolled.mp4 -t 00:00:30 shortSmallRolled.mp4
# Export video frames with the desired frame rate:
ffmpeg -i shortSmallRolled.mp4 -r 35/1 frames/frame-%03d.bmp

I gathered the resulting frame images in an array and went ahead to build a script to get the frames into JavaScript. A friend found the pixel-bmp library which would come in handy in parsing the frame data. I later discovered that the uncompressed data led to too big of a script so that I decided to drop every second frame. From this I came up with the following code:

const frameNames = require("./framenames.json");
const fileNames = frameNames.map((f) => `./frames/${f}`);

const pixelBitmap = require("pixel-bmp");

const fData = [];

const finish = Promise.all(
  fileNames.map((fName, fIndex) => {
    if (fIndex % 2 === 0) {
      return;
    }

    return pixelBitmap.parse(fName).then(([image]) => {
      const frame = [];

      for (let i = 0; i < image.data.length; i += 4) {
        frame.push({
          r: image.data[i],
          g: image.data[i + 1],
          b: image.data[i + 2],
        });
      }

      fData[Math.floor(fIndex / 2)] = frame;
    });
  })
);

I later discovered that the image as read from the frames needed to be transposed to prevent ending up with weird images and it took me a moment to get a function to correct for the indices:

function frameMod(i, sliceSize, total) {
  const drift = i * sliceSize;
  const rem = Math.floor(drift / total);

  return Math.floor((i * sliceSize + rem) % total);
}

The thing particularly nice about frameMod was that it was easily testable because I knew the index mapping needed to look like this:

[ 0, 1, 2,
  3, 4, 5,
  6, 7, 8,
].map(i => frameMod(i, 3, 9)) ≣ [
  0, 3, 6,
  1, 4, 7,
  2, 5, 8,
];

Next I would just print the data to console and pipe that into a file:

finish.then(() => {
  const transposedFrameData = fData.map((frame) => {
    const transposedFrame = [];

    for (let i = 0; i < frame.length; i++) {
      const px = frame[frameMod(i, 44, frame.length)];

      transposedFrame.push(px.r, px.g, px.b);
    }

    return transposedFrame;
  });

  console.log(`const frameData = ${JSON.stringify(transposedFrameData)};`);
});

Time to stick it together! Instead of interpolating between frames I decided to just show every frame twice to accommodate for the dropped frames.

const frameData = require("./frameData");

function writeFrame(frameIndex, frame) {
  const wantedFrame = frameData[Math.floor(frameIndex / 2)] || [];

  for (let i = 0; i < frame.length; i++) {
    frame[i] =
      wantedFrame.length > 0 ? wantedFrame[i] : Math.floor(Math.random() * 255);
  }
}

export function transform(buffer, rows, cols, frameCount, fps, isFirst) {
  const frameSize = 3 * rows * cols;
  for (let i = 0; i < frameCount; i++) {
    const frame = new Uint8Array(buffer, frameSize * i, frameSize);
    writeFrame(i, frame);
  }
}

export default function () {
  return Promise.resolve({
    transform,
  });
}

Half a day of hunting strange bugs later there was one more conference that’s not a stranger to love.

You know the rules and so do I…

📹 Embed Video from:

👁️ Watch on: youtube.com

📋 Copy link to:

📜 Read the Google Privacy Policy

Getting sidetracked with the Y-Combinator

Picture of JavaScript code for a y-combinator stamped onto a brown paper bag At the booth of SinnerSchrader they offered the chance to play with stamps and customize a paper bag that one could fill with snacks afterwards.

I had loads of fun trying to imprint a JavaScript y-combinator onto a paper bag.

The source code I was going for should have looked like this:

const Y = (F) => ((x) => x(x))((x) => F((y) => x(x)(y)));

Without the excessive parenthesis you can end up with an example a bit more clearer which I found on blog.klipse.tech:

const Y = (f) => ((x) => x(x))((x) => f((y) => x(x)(y)));

Here are some example usages:

const fac = Y((f) => (n) => (n === 0 ? 1 : n * f(n - 1)));
// fac(5) -> 120
const fib = Y((f) => (n) => (n < 2 ? n : f(n - 1) + f(n - 2)));
// [0, 1, 2, 3, 4, 5, 6, 7, 8].map(fib) -> [0, 1, 1, 2, 3, 5, 8, 13, 21]

The real beauty behind this is that recursion itself can be abstracted away and we could even enhance the calls of f to add additional magic. Indeed blog.klipse.tech also has a nice example of this where they demonstrate how one can use this to add memoization.

Exiting JsConfEu