|  | const transform = { | 
        
          |  | translate(x, y) { | 
        
          |  | return transformFunction('translate', x, y); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function transformFunction(property, ...values) { | 
        
          |  | return `${property}(${values.join(', ')})`; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function px(unit) { | 
        
          |  | return `${unit}px`; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function add(a, b) { | 
        
          |  | const result = Array(a.length).fill(0); | 
        
          |  |  | 
        
          |  | for (let i = 0; i < a.length; i++) { | 
        
          |  | result[i] = a[i] + b[i]; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | return result; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // particle | 
        
          |  |  | 
        
          |  | // particle() -> DOM node | 
        
          |  | function particle() { | 
        
          |  | const node = document.createElement('div'); | 
        
          |  |  | 
        
          |  | node.style.background = 'black'; | 
        
          |  | node.style.position = 'absolute'; | 
        
          |  | node.style.width = px(50); | 
        
          |  | node.style.height = px(50); | 
        
          |  | node.style.borderRadius = '50%'; | 
        
          |  |  | 
        
          |  | return node; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // insert(node, position: vector) -> DOM node | 
        
          |  | function insert(node, position) { | 
        
          |  | const { translate } = transform; | 
        
          |  |  | 
        
          |  | node.style.transform = | 
        
          |  | translate(...position.map((p) => p - 25).map(px)); | 
        
          |  |  | 
        
          |  | return document.body.appendChild(node); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function place(node, position) { | 
        
          |  | const { translate } = transform; | 
        
          |  |  | 
        
          |  | node.style.transform = | 
        
          |  | translate(...position.map((p) => p - 25).map(px)); | 
        
          |  |  | 
        
          |  | return node; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // position(node) -> vector | 
        
          |  | function position(node) { | 
        
          |  | const translate = node.style.transform, | 
        
          |  | p = translate.slice(10, translate.length - 1); | 
        
          |  |  | 
        
          |  | return p.split(',').map((s) => parseInt(s.trim(), 10)); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // remove(node) -> node | 
        
          |  | function remove(node) { | 
        
          |  | return node.parentNode.removeChild(node); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // shift(node, to: vector) -> node | 
        
          |  | function shift(node, to) { | 
        
          |  | const { translate } = transform; | 
        
          |  |  | 
        
          |  | node.style.transform = translate(...to.map(px)); | 
        
          |  |  | 
        
          |  | return node; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // move(node, velocity: vector) -> null | 
        
          |  | function move(node, velocity) { | 
        
          |  | const shifted = shift(node, add(position(node), velocity)); | 
        
          |  |  | 
        
          |  | requestAnimationFrame( | 
        
          |  | move.bind(null, shifted, velocity)); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function speed([a, b]) { | 
        
          |  | const { pow, sqrt } = Math; | 
        
          |  |  | 
        
          |  | return sqrt(pow(a, 2) + pow(b, 2)); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function direction([a, b]) { | 
        
          |  | return deg(Math.atan2(a, b)); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function deg(radian) { | 
        
          |  | return radian * (180 / Math.PI); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function rad(degree) { | 
        
          |  | return degree * (Math.PI / 180); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // magnitude: scalar, direction: rad -> vector | 
        
          |  | function vector(magnitude, direction) { | 
        
          |  | const a = Math.cos(direction) * magnitude, | 
        
          |  | b = Math.sin(direction) * magnitude; | 
        
          |  |  | 
        
          |  | return [a, b]; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function arc(magnitude) { | 
        
          |  | const node = insert(particle(), vector(magnitude, rad(0))); | 
        
          |  | let direction = 0, increasing = true; | 
        
          |  |  | 
        
          |  | return function loop() { | 
        
          |  | place(node, vector(magnitude, rad(direction))); | 
        
          |  |  | 
        
          |  | if (direction === 0 && !increasing) { | 
        
          |  | increasing = true; | 
        
          |  | } | 
        
          |  | else if (direction === 90) { | 
        
          |  | increasing = false; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | direction = increasing ? direction + 1 : direction - 1; | 
        
          |  |  | 
        
          |  | requestAnimationFrame(loop); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | arc(50)(); | 
        
          |  | arc(150)(); | 
        
          |  | arc(250)(); | 
        
          |  | arc(350)(); | 
        
          |  | arc(450)(); | 
        
          |  | arc(550)(); | 
        
          |  | arc(650)(); |