we don't make mistakes at DevOne 2019
Giving the closing keynote DEV ONE 2019, held on April 11 in Linz, Austria was absolutely fantastic. The conference was very well organized with one track, 11 talks about "scale" and 600 wonderful delegates. I learnt so much that day, got confirmation for some good practices but also learnt about many opportunities to improve our work. Also it was a pleasure to meet new people and see familiar faces among the organizers and attendees from Script'17.
We don't make mistakes
For this keynote named after the adage from Bob Ross and the Joy of painting, I wanted to talk about three aspects of my life as a developer: work at Microsoft, some demoscene projects and the Code Club I run with children aged 7-12. And highlight how important it is to give yourself a space where there are no mistakes; only happy accidents. Illustrating this idea with a LIVE coding session recreating a demoscene project and some lessons I learnt from this and applied directly at work.
Video, slides and LIVE coding
Here are the video recordings of all the talks, my slides and a slightly polished version of the LIVE coding I did during the talk.
LIVE coding and notes
You can see the result of the LIVE coding session, the entire code but also the notes I wrote for this session since a few folks curious about that aspect of the talk.
Code
<!DOCTYPE html>
<link rel="stylesheet" href="style.css" />
<body>
<img src="stars1.png" />
<img src="stars2.png" />
<img src="stars3.png" />
<img src="stars4.png" />
<img src="stars5.png" />
<img src="stars6.png" />
<img src="stars7.png" />
<img src="stars8.png" />
<canvas></canvas>
</body>
<script>
const c = document.querySelector("canvas");
const ctx = c.getContext("2d");
const cols = 64;
const rows = 32;
const colors = ["transparent", "#f0c3", "#0fc6", "#fff"];
// create cells
const cellsCount = rows * cols;
const cells = new Array(cellsCount).fill(0);
let cellCurrentIndex = cellsCount;
let chars = "";
for (let i = 0; i < 64; i++) {
chars += String.fromCharCode(0x2580 + i);
}
const renderFrame = timeNow => {
requestAnimationFrame(renderFrame);
c.width = (cols + 2) * 10;
c.height = (rows + 2) * 10;
ctx.textBaseline = "top";
ctx.textAlign = "center";
// prepare for the rotozoom
const zoom = 16 + 4 * Math.sin(timeNow / 2329);
const angle = timeNow / 9999;
const cos = Math.cos(angle) / zoom;
const sin = Math.sin(angle) / zoom;
// cellular automaton
let rule = timeNow / 270; // change rule every few ms
rule &= 52 | 30; // limit the set of rules
// move to the next row every few ms
const cellNextIndex = timeNow / 70 * cols;
for (; cellCurrentIndex < cellNextIndex; cellCurrentIndex++) {
const glitch = Math.random() * 1.01;
// check the neighbors
const neighbors =
cells[(cellCurrentIndex - 1) % cellsCount] +
2 * cells[cellCurrentIndex % cellsCount] +
4 * cells[(cellCurrentIndex + 1) % cellsCount];
// compute the state of the cell for the next generation / row
const something = ((rule >> neighbors) & 1) ^ glitch;
cells[(cellCurrentIndex + cols) % cellsCount] = something;
}
// draw all the characters on our canvas
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const index = x + y * cols;
const glitch = Math.random() * 1.01;
// base charIndex
let charIndex = x / 4 + timeNow / 300 + glitch;
// rotated x,y coordinates
const x2 = x * cos - y * sin + 5;
const y2 = x * sin + y * cos + 5;
// set of patterns
const patterns = [x2 ^ y2, x2 % y2, x2 | y2, x2 & y2];
// pick one every few ms
const pattern = patterns[(timeNow / 1978) & 3];
// base color index
let colorIndex = pattern & 1;
// use the cellular automat to change the colorIndex & charIndex
colorIndex += 2 * cells[index];
charIndex += 16 * cells[index];
// render the character
ctx.fillStyle = colors[colorIndex];
const char = chars[charIndex & 63];
ctx.fillText(char, (x + 1) * 10, (y + 1) * 10);
}
}
// bloom
ctx.filter = "blur(4px)";
ctx.drawImage(c, 0, 0);
ctx.filter = "blur(0px)";
};
renderFrame(0);
</script>
Notes
This is all the notes I had for this session. These are mostly to get an idea of the pacing and some small things that I shouldn't forget. You can imagine that it is easy to lose track when pair programming with 600 people.
show the images
zoom animation
add the animation delays
tilt animation
remove the outlines
start code
querySelector, getContext renderFrame = timeNow => {} requestAnimationFrame
draw the time to check it works
cols=64 rows=32 -> width and height
ctx.textBaseline = "top" ctx.textAlign = "center"
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
char = String.fromCharCoded(Math.random()*256);
ctx.fillText(char, (1+x)*10,(1+y)*10);
}
}
build list of chars from 0x2580 because it looks cool
let charIndex = (x / 8 + glitch + timeNow / 599) & 31;
Let's start the rotozoom
first a pattern x^y&1
zoom = 8
colorIndex = (x/zoom)^(y/zoom) & 1
colors = ["transparent", "#f0c", "#c0f6", "#fcf", "#f0c", "#fff"];
Introduce multiple patterns
[x^y, x%y, x|y, x&y]
rotozoom time
Add little glitch
add bloom
ctx.filter = "blur(4px)";
ctx.drawImage(c, 0, 0);
ctx.filter = "blur(0px)";
cellular automaton
const life = new Array(cols * rows).fill(0);
let lifeCurrentIndex = 0;
let lifeCurrentRule = 17;
lifeCurrentNextIndex = timeNow * rows / 12942
for(;lifeCurrentIndex<lifeNextIndex; lifeCurrentIndex++){
life[(lifeCurrentIndex + rows) % life.length] = ...
}
colorIndex += 2*life[i];
charIndex |= life[i]*16
Feedback
The feedback right after the keynote was great. Several delegates and speakers, shared their inspiration from this session, and I hope you will appreciate this talk. In any case, let me know what you think on Twitter, email or in the video.