├── LICENSE ├── README.md ├── app.js.oak ├── bundle.js ├── img ├── screenshot-full.jpg └── screenshot-thumb.jpg └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Linus Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # burds! 🐦 2 | 3 | [**burds!**](https://burds.vercel.app/) is a weekend hack inspired by [this fabulous tweet](https://twitter.com/LoekVugs/status/1488140798671663104). It's a little web experiment with tiny animated birds jumping around their tiny little world. If you like this project, you may also like [Tiny Humans](https://tinyhumans.vercel.app/). 4 | 5 | **burds!** is written entirely in [Oak](https://oaklang.org), my toy programming language that conveniently compiles to JavaScript. Everything except the title text is rendered using 2D Canvas APIs. 6 | 7 | ![Screenshot of burds](/img/screenshot-full.jpg) 8 | 9 | ## Development 10 | 11 | **burds!** is a static, single-page app. It lives in `index.html`. Development is done using the `oak` CLI, which you can get from [oaklang.org](https://oaklang.org/#start). 12 | 13 | To autoformat the source file: 14 | 15 | ```sh 16 | oak fmt app.js.oak --fix 17 | ``` 18 | 19 | To recompile the JavaScript bundle from [Oak](https://oaklang.org) sources: 20 | 21 | ```sh 22 | oak build --entry app.js.oak -o bundle.js --web 23 | ``` 24 | 25 | Usually it's convenient to automatically do this on every save of `app.js.oak` (with [entr](https://eradman.com/entrproject/)): 26 | 27 | ```sh 28 | ls *.oak | entr -cr oak build --entry app.js.oak -o bundle.js --web 29 | ``` 30 | 31 | _Note: As of the first release of this repository, the latest versioned Oak release is missing some standard library APIs (`math.{sqrt, hypot, scale, bearing, orient}`), so for now, you may have to build Oak from source (which is in Go) to make changes to this project. Brave souls can look more into that in the [Oak repository](https://github.com/thesephist/oak)._ 32 | -------------------------------------------------------------------------------- /app.js.oak: -------------------------------------------------------------------------------- 1 | // making this -> https://twitter.com/LoekVugs/status/1488140798671663104 2 | 3 | { 4 | println: println 5 | default: default 6 | range: range 7 | slice: slice 8 | map: map 9 | each: each 10 | merge: merge 11 | filter: filter 12 | append: append 13 | } := import('std') 14 | math := import('math') 15 | sort := import('sort') 16 | random := import('random') 17 | 18 | // rendering configs 19 | DPI := window.devicePixelRatio |> default(1) 20 | // every measurement of distance or length is measured as a multiple of some 21 | // distance-adjusted scale, to allow for easily experimenting with burd size 22 | // and perspective. MaxScale sets the scaling multiplier at the "front" of the 23 | // scene. 24 | MaxScale := 5 25 | JumpDuration := 0.25 // seconds 26 | JumpHeight := 0.5 27 | JumpDistance := 12 28 | // To reduce visual clutter, we remove footprints that are old. On average, 29 | // each burd leaves FootprintTrail number of footprints before they are erased. 30 | FootprintTrail := 8 31 | // shorthand for convenient access 32 | DrawWidth := window.innerWidth 33 | DrawHeight := window.innerHeight 34 | 35 | // Canvas bindings 36 | Canvas := document.querySelector('canvas') 37 | Ctx := Canvas.getContext('2d') 38 | 39 | // angle takes an angle in degrees, and returns radians 40 | fn angle(deg) deg / 180 * math.Pi 41 | 42 | // normAngle takes an angle in radians and returns an equivalent angle in the 43 | // range [0, 2 * math.Pi). 44 | fn normAngle(t)(t + 2 * math.Pi) % (2 * math.Pi) 45 | 46 | // squish is used to "rotate" the birds' geometries so they look like they're 47 | // facing some angle. It's not exact, but it's quite cheap and simple compared 48 | // to doing a full 3D projection and does the job. 49 | // 50 | // Note that we can't do a full 3D rotation because burds are 2D and rotating 51 | // them to 90deg would make them a zero-width line. 52 | fn squish(point, center, t) { 53 | [x, y] := point 54 | [cx, cy] := center 55 | [ 56 | cx + (x - cx) * cos(t) 57 | cy + (y - cy) * (1 + 0.9 * math.abs(sin(t))) 58 | ] 59 | } 60 | 61 | // line takes two points and draws a line between them 62 | fn line(start, end) { 63 | Ctx.beginPath() 64 | Ctx.moveTo(start.0, start.1) 65 | Ctx.lineTo(end.0, end.1) 66 | Ctx.stroke() 67 | } 68 | 69 | // ellipse draws a filled ellipse with no rotation 70 | fn ellipse(center, rx, ry) { 71 | Ctx.beginPath() 72 | Ctx.ellipse(center.0, center.1, rx, ry, 0, 0, 2 * math.Pi) 73 | Ctx.fill() 74 | } 75 | 76 | // arcBetween draws an arc connected between two points, with a specified 77 | // radius. dir must be :clock or :counterclock. mode can be :stroke or :fill. 78 | // 79 | // arcBetween is more suitable for our purposes than the HTML Canvas's built-in 80 | // CanvasContext2D.arcTo() method in that this avoids the large-radius issue 81 | // (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo#result_of_a_large_radius). 82 | // 83 | // Center-finding algorithm based on: 84 | // https://math.stackexchange.com/questions/27535/how-to-find-center-of-an-arc-given-start-point-end-point-radius-and-arc-direc 85 | fn arcBetween(start, end, radius, dir, mode) { 86 | epsilon := if dir { 87 | :counterclock -> 1 88 | :clock -> -1 89 | } 90 | dist := math.hypot(start.0, start.1, end.0, end.1) 91 | arcdist := math.sqrt(radius * radius - dist * dist / 4) 92 | u := (end.0 - start.0) / dist 93 | v := (end.1 - start.1) / dist 94 | cx := (start.0 + end.0) / 2 - epsilon * arcdist * v 95 | cy := (start.1 + end.1) / 2 + epsilon * arcdist * u 96 | 97 | startAngle := math.orient(cx, cy, start.0, start.1) 98 | endAngle := math.orient(cx, cy, end.0, end.1) 99 | 100 | Ctx.beginPath() 101 | if dir { 102 | :counterclock -> Ctx.arc(cx, cy, radius, startAngle, endAngle) 103 | :clock -> Ctx.arc(cx, cy, radius, endAngle, startAngle) 104 | } 105 | if mode { 106 | :fill -> Ctx.fill() 107 | :stroke -> Ctx.stroke() 108 | } 109 | } 110 | 111 | fn drawFootprint(fp) { 112 | Scale := fp.y / DrawHeight * MaxScale 113 | FpRadius := 1 * Scale 114 | legSpan := 1.5 * Scale 115 | 116 | ellipse([fp.x + legSpan, fp.y], FpRadius, FpRadius / 2) 117 | ellipse([fp.x - legSpan, fp.y], FpRadius, FpRadius / 2) 118 | } 119 | 120 | fn drawShadow(burd) { 121 | { 122 | x: x 123 | y: y 124 | z: z 125 | direction: dir 126 | } := burd 127 | 128 | Scale := y / DrawHeight * MaxScale 129 | shadowRadiusX := 4 * Scale * (1 + 0.5 * math.abs(cos(dir))) 130 | shadowRadiusY := 2 * Scale 131 | 132 | ellipse([x, y - z], shadowRadiusX, shadowRadiusY) 133 | } 134 | 135 | fn drawBurd(burd) { 136 | { 137 | x: x 138 | y: y 139 | z: z 140 | direction: dir 141 | color: color 142 | } := burd 143 | 144 | Scale := math.scale(y, 0, DrawHeight, 0.1, 1) * MaxScale 145 | 146 | facing := if { 147 | dir < angle(70) 148 | dir > angle(360 - 70) -> :east 149 | dir < angle(180 - 70) -> :north 150 | dir < angle(180 + 70) -> :west 151 | _ -> :south 152 | } 153 | 154 | legSpan := 1.5 * Scale 155 | legLength := 6 * Scale 156 | bodyCenter := [ 157 | x + if facing { 158 | :east -> -1 * Scale 159 | :west -> 1 * Scale 160 | _ -> 0 161 | } 162 | y - 11 * Scale 163 | ] 164 | bodyHalfLength := 8 * Scale 165 | attitude := angle(30) 166 | headPoint := math.bearing(bodyCenter.0, bodyCenter.1, bodyHalfLength, -attitude) |> squish(bodyCenter, dir) 167 | tailPoint := math.bearing(bodyCenter.0, bodyCenter.1, bodyHalfLength, -attitude + math.Pi) |> squish(bodyCenter, dir) 168 | headLength := 8 * Scale 169 | headAtt := if facing { 170 | :east -> angle(-30) 171 | :west -> angle(180 + 30) 172 | _ -> if { 173 | dir < angle(90) 174 | dir > angle(270) -> angle(-10) 175 | _ -> angle(180 + 10) 176 | } 177 | } 178 | headEnd := math.bearing(headPoint.0, headPoint.1, headLength, -headAtt) 179 | roundBellyRadius := 8.75 * Scale 180 | flatBellyRadius := 25 * Scale 181 | midBellyRadius := 10.5 * Scale 182 | [clockBodyRadius, counterclockBodyRadius] := if facing { 183 | :east -> [flatBellyRadius, roundBellyRadius] 184 | :west -> [roundBellyRadius, flatBellyRadius] 185 | _ -> [midBellyRadius, midBellyRadius] 186 | } 187 | 188 | :legs, { 189 | Ctx.lineWidth := Scale / 2 190 | line([x + legSpan, y], [x + legSpan, y - legLength]) 191 | line([x - legSpan, y], [x - legSpan, y - legLength]) 192 | } 193 | 194 | :body, { 195 | Ctx.fillStyle := color 196 | arcBetween(headPoint, tailPoint, clockBodyRadius, :clock, :fill) 197 | arcBetween(headPoint, tailPoint, counterclockBodyRadius, :counterclock, :fill) 198 | arcBetween(headPoint, tailPoint, clockBodyRadius, :clock, :stroke) 199 | arcBetween(headPoint, tailPoint, counterclockBodyRadius, :counterclock, :stroke) 200 | } 201 | 202 | :head, { 203 | line(headPoint, headEnd) 204 | } 205 | } 206 | 207 | // midJumpBurd maps a burd with a jump time to a burd with its x, y, z adjusted 208 | // to reflect its actual mid-jump position. For burds that are in the process 209 | // of jumping, midJumpBurd is called each frame to compute its position 210 | // mid-jump rather than updating its actual properties every frame. This helps 211 | // keep the burd's state functional, flowing from a single source of truth, 212 | // which is the jump time (burd.jumped). 213 | fn midJumpBurd(burd) { 214 | { 215 | x: x 216 | y: y 217 | direction: dir 218 | jumped: jumped 219 | color: color 220 | } := burd 221 | 222 | Scale := y / DrawHeight * MaxScale 223 | 224 | t := time() |> math.scale(jumped, jumped + JumpDuration) 225 | [destX, destY] := math.bearing(x, y, JumpDistance * Scale, -dir) 226 | 227 | // the jump height (parabolic component of the burd's motion) is computed 228 | // by a parabolic function based on the jump distance and its progress 229 | // through that parabola. 230 | c := 4 * JumpHeight * Scale / JumpDuration / JumpDuration 231 | jumpY := c * t * (t - 1) 232 | 233 | // x moves linearly 234 | movingX := t |> math.scale(0, 1, x, destX) 235 | // y has a linear component and a parabolic component 236 | movingY := t |> math.scale(0, 1, y, destY) + jumpY 237 | 238 | { 239 | x: movingX 240 | y: movingY 241 | z: jumpY 242 | direction: dir 243 | color: color 244 | } 245 | } 246 | 247 | // main draw call, called every frame 248 | fn draw { 249 | // get a fresh canvas 250 | Ctx.setTransform(DPI, 0, 0, DPI, 0, 0) 251 | Ctx.clearRect(0, 0, Canvas.width, Canvas.height) 252 | 253 | // general canvas config 254 | Ctx.lineCap := 'round' 255 | Ctx.lineJoin := 'round' 256 | Ctx.strokeStyle := '#000000' 257 | 258 | // setup for footprints 259 | Ctx.fillStyle := '#cccccc' 260 | Footprints |> each(drawFootprint) 261 | 262 | // to render burds, first we need to compute their true (x, y) if they are 263 | // mid-jump. 264 | JumpedBurds := Burds |> map(fn(burd) if burd.jumped { 265 | ? -> burd 266 | _ -> midJumpBurd(burd) 267 | }) 268 | 269 | // We first draw all shadows, then all burds, so shadows do not occlude 270 | // burds. 271 | Ctx.fillStyle := 'rgba(0, 0, 0, 0.2)' // shadows are all one color 272 | JumpedBurds |> each(drawShadow) 273 | // we want to sort them by their "closeness" to the viewer, not their Y. 274 | // Closeness is the y of the shadow, or y - z (z being the jump height). 275 | JumpedBurds |> sort.sort(fn(b) b.y - b.z) |> each(drawBurd) 276 | } 277 | 278 | // handleResize resets and re-renders the canvas when something about the 279 | // window viewport geometry changes. 280 | fn handleResize { 281 | DrawWidth <- window.innerWidth 282 | DrawHeight <- window.innerHeight 283 | 284 | Canvas.width := int(DrawWidth * DPI) 285 | Canvas.height := int(DrawHeight * DPI) 286 | Canvas.style.width := string(DrawWidth) + 'px' 287 | Canvas.style.height := string(DrawHeight) + 'px' 288 | 289 | draw() 290 | } 291 | 292 | // initial simulation states 293 | BurdPopulation := (DrawWidth * DrawHeight / 50000) |> 294 | // min 6, max 50 burds, for perf/visual clutter balance 295 | math.min(50) |> 296 | math.max(6) 297 | Burds := range(BurdPopulation) |> map(fn(n) { 298 | // x and y denote their pixel position on the canvas 299 | x: random.number(5, DrawWidth - 5) 300 | y: random.number(5, DrawHeight - 5) 301 | // z is the vertical distance between a burd and its shadow, so named 302 | // because conceptually it's the "z" distancae of a burd from the "ground". 303 | z: 0 304 | // the direction the burd is facing, in radians 305 | direction: random.number(0, 2 * math.Pi) 306 | color: random.choice([ 307 | '#000000' 308 | '#f0eff1' 309 | ]) 310 | }) 311 | Footprints := [] 312 | 313 | // jumpRandomBurd picks a random burd that's not mid-jump and starts its jump 314 | // by setting its burd.jumped timstamp. 315 | fn jumpRandomBurd { 316 | if ? != burd := random.choice(Burds |> filter(fn(b) b.jumped = ?)) -> { 317 | burd.jumped := time() 318 | 319 | // add its old footprint (from where it jumps) to the footprints list 320 | Footprints << { x: burd.x, y: burd.y } 321 | MaxFootprints := int(BurdPopulation * FootprintTrail) 322 | if len(Footprints) > MaxFootprints -> { 323 | Footprints <- Footprints |> slice(len(Footprints) - MaxFootprints) 324 | } 325 | 326 | // when the jump is done, schedule a task to move its (x, y) to its 327 | // destination coordinates post-jump. 328 | with wait(JumpDuration) fn { 329 | Scale := burd.y / DrawHeight * MaxScale 330 | [x, y] := math.bearing(burd.x, burd.y, JumpDistance * Scale, -burd.direction) 331 | 332 | // we never want burds to completely leave the screen, so that there's 333 | // a roughly constant density of burds on screen over time. To do 334 | // this, we direct the burds towards the center of the canvas when 335 | // burds are about to leave (within 50px of the canvas bounds). 336 | direction := if { 337 | x < 50, x > DrawWidth - 50 338 | y < 50, y > DrawHeight - 50 -> math.orient(x, -y, DrawWidth / 2, -DrawHeight / 2) 339 | _ -> burd.direction + random.number(angle(-30), angle(30)) 340 | } |> normAngle() 341 | 342 | burd |> merge({ 343 | x: x 344 | y: y 345 | direction: direction 346 | jumped: ? 347 | }) 348 | } 349 | } 350 | } 351 | 352 | // draw every frame 353 | fn startDrawing { 354 | draw() 355 | requestAnimationFrame(startDrawing) 356 | } 357 | 358 | // jump every N seconds, where N is determined by the total number of burds, so 359 | // burds on average jump pretty frequently. 1 is a magic number here determined 360 | // by trial and error. 361 | fn startJumping { 362 | with wait(1 / BurdPopulation) fn { 363 | jumpRandomBurd() 364 | startJumping() 365 | } 366 | } 367 | 368 | // main 369 | handleResize() 370 | window.addEventListener('resize', handleResize) 371 | with document.querySelector('.hideButton').addEventListener('click') fn { 372 | document.querySelector('#attribution').style.display := 'none' 373 | } 374 | startDrawing() 375 | startJumping() 376 | 377 | -------------------------------------------------------------------------------- /bundle.js: -------------------------------------------------------------------------------- 1 | /* oak build --web */ 2 | // module system 3 | const __Oak_Modules = {}; 4 | let __Oak_Import_Aliases; 5 | function __oak_modularize(name, fn) { 6 | __Oak_Modules[name] = fn; 7 | } 8 | function __oak_module_import(name) { 9 | if (typeof __Oak_Modules[name] === 'object') return __Oak_Modules[name]; 10 | const module = __Oak_Modules[name] || __Oak_Modules[__Oak_Import_Aliases[name]]; 11 | if (module) { 12 | __Oak_Modules[name] = {}; // break circular imports 13 | return __Oak_Modules[name] = module(); 14 | } else { 15 | throw new Error(`Could not import Oak module "${name}" at runtime`); 16 | } 17 | } 18 | 19 | // language primitives 20 | let __oak_empty_assgn_tgt; 21 | function __oak_eq(a, b) { 22 | if (a === __Oak_Empty || b === __Oak_Empty) return true; 23 | 24 | // match either null or undefined to compare correctly against undefined ?s 25 | // appearing in places like optional arguments 26 | if (a == null && b == null) return true; 27 | if (a == null || b == null) return false; 28 | 29 | // match all other types that can be compared cheaply (without function 30 | // calls for type coercion or recursive descent) 31 | if (typeof a === 'boolean' || typeof a === 'number' || 32 | typeof a === 'symbol' || typeof a === 'function') { 33 | return a === b; 34 | } 35 | 36 | // string equality check 37 | a = __as_oak_string(a); 38 | b = __as_oak_string(b); 39 | if (typeof a !== typeof b) return false; 40 | if (__is_oak_string(a) && __is_oak_string(b)) { 41 | return a.valueOf() === b.valueOf(); 42 | } 43 | 44 | // deep equality check for composite values 45 | if (len(a) !== len(b)) return false; 46 | for (const key of keys(a)) { 47 | if (!__oak_eq(a[key], b[key])) return false; 48 | } 49 | return true; 50 | } 51 | function __oak_acc(tgt, prop) { 52 | return (__is_oak_string(tgt) ? __as_oak_string(tgt.valueOf()[prop]) : tgt[prop]) ?? null; 53 | } 54 | function __oak_obj_key(x) { 55 | return typeof x === 'symbol' ? Symbol.keyFor(x) : x; 56 | } 57 | function __oak_push(a, b) { 58 | a = __as_oak_string(a); 59 | a.push(b); 60 | return a; 61 | } 62 | function __oak_and(a, b) { 63 | if (typeof a === 'boolean' && typeof b === 'boolean') { 64 | return a && b; 65 | } 66 | if (__is_oak_string(a) && __is_oak_string(b)) { 67 | const max = Math.max(a.length, b.length); 68 | const get = (s, i) => s.valueOf().charCodeAt(i) || 0; 69 | 70 | let res = ''; 71 | for (let i = 0; i < max; i ++) { 72 | res += String.fromCharCode(get(a, i) & get(b, i)); 73 | } 74 | return res; 75 | } 76 | return a & b; 77 | } 78 | function __oak_or(a, b) { 79 | if (typeof a === 'boolean' && typeof b === 'boolean') { 80 | return a || b; 81 | } 82 | if (__is_oak_string(a) && __is_oak_string(b)) { 83 | const max = Math.max(a.length, b.length); 84 | const get = (s, i) => s.valueOf().charCodeAt(i) || 0; 85 | 86 | let res = ''; 87 | for (let i = 0; i < max; i ++) { 88 | res += String.fromCharCode(get(a, i) | get(b, i)); 89 | } 90 | return res; 91 | } 92 | return a | b; 93 | } 94 | function __oak_xor(a, b) { 95 | if (typeof a === 'boolean' && typeof b === 'boolean') { 96 | return (a && !b) || (!a && b); 97 | } 98 | if (__is_oak_string(a) && __is_oak_string(b)) { 99 | const max = Math.max(a.length, b.length); 100 | const get = (s, i) => s.valueOf().charCodeAt(i) || 0; 101 | 102 | let res = ''; 103 | for (let i = 0; i < max; i ++) { 104 | res += String.fromCharCode(get(a, i) ^ get(b, i)); 105 | } 106 | return res; 107 | } 108 | return a ^ b; 109 | } 110 | const __Oak_Empty = Symbol('__Oak_Empty'); 111 | 112 | // mutable string type 113 | function __is_oak_string(x) { 114 | if (x == null) return false; 115 | return x.__mark_oak_string; 116 | } 117 | function __as_oak_string(x) { 118 | if (typeof x === 'string') return __Oak_String(x); 119 | return x; 120 | } 121 | const __Oak_String = s => { 122 | return { 123 | __mark_oak_string: true, 124 | assign(i, slice) { 125 | if (i === s.length) return s += slice; 126 | return s = s.substr(0, i) + slice + s.substr(i + slice.length); 127 | }, 128 | push(slice) { 129 | s += slice; 130 | }, 131 | toString() { 132 | return s; 133 | }, 134 | valueOf() { 135 | return s; 136 | }, 137 | get length() { 138 | return s.length; 139 | }, 140 | } 141 | } 142 | 143 | // tail recursion trampoline helpers 144 | function __oak_resolve_trampoline(fn, ...args) { 145 | let rv = fn(...args); 146 | while (rv && rv.__is_oak_trampoline) { 147 | rv = rv.fn(...rv.args); 148 | } 149 | return rv; 150 | } 151 | function __oak_trampoline(fn, ...args) { 152 | return { 153 | __is_oak_trampoline: true, 154 | fn: fn, 155 | args: args, 156 | } 157 | } 158 | 159 | // env (builtin) functions 160 | 161 | // reflection and types 162 | const __Is_Oak_Node = typeof process === 'object'; 163 | const __Oak_Int_RE = /^[+-]?\d+$/; 164 | function int(x) { 165 | x = __as_oak_string(x); 166 | if (typeof x === 'number') { 167 | // JS rounds towards higher magnitude, Oak rounds towards higher value 168 | const rounded = Math.floor(x); 169 | const diff = x - rounded; 170 | if (x < 0 && diff === 0.5) return rounded + 1; 171 | return rounded; 172 | } 173 | if (__is_oak_string(x) && __Oak_Int_RE.test(x.valueOf())) { 174 | const i = Number(x.valueOf()); 175 | if (isNaN(i)) return null; 176 | return i; 177 | } 178 | return null; 179 | } 180 | function float(x) { 181 | x = __as_oak_string(x); 182 | if (typeof x === 'number') return x; 183 | if (__is_oak_string(x)) { 184 | const f = parseFloat(x.valueOf()); 185 | if (isNaN(f)) return null; 186 | return f; 187 | } 188 | return null; 189 | } 190 | function atom(x) { 191 | x = __as_oak_string(x); 192 | if (typeof x === 'symbol' && x !== __Oak_Empty) return x; 193 | if (__is_oak_string(x)) return Symbol.for(x.valueOf()); 194 | return Symbol.for(string(x)); 195 | } 196 | function string(x) { 197 | x = __as_oak_string(x); 198 | function display(x) { 199 | x = __as_oak_string(x); 200 | if (__is_oak_string(x)) { 201 | return '\'' + x.valueOf().replace('\\', '\\\\').replace('\'', '\\\'') + '\''; 202 | } else if (typeof x === 'symbol') { 203 | if (x === __Oak_Empty) return '_'; 204 | return ':' + Symbol.keyFor(x); 205 | } 206 | return string(x); 207 | } 208 | if (x == null) { 209 | return '?'; 210 | } else if (typeof x === 'number') { 211 | return x.toString(); 212 | } else if (__is_oak_string(x)) { 213 | return x; 214 | } else if (typeof x === 'boolean') { 215 | return x.toString(); 216 | } else if (typeof x === 'function') { 217 | return x.toString(); 218 | } else if (typeof x === 'symbol') { 219 | if (x === __Oak_Empty) return '_'; 220 | return Symbol.keyFor(x); 221 | } else if (Array.isArray(x)) { 222 | return '[' + x.map(display).join(', ') + ']'; 223 | } else if (typeof x === 'object') { 224 | const entries = []; 225 | for (const key of keys(x).sort()) { 226 | entries.push(`${key}: ${display(x[key])}`); 227 | } 228 | return '{' + entries.join(', ') + '}'; 229 | } 230 | throw new Error('string() called on unknown type ' + x.toString()); 231 | } 232 | function codepoint(c) { 233 | c = __as_oak_string(c); 234 | return c.valueOf().charCodeAt(0); 235 | } 236 | function char(n) { 237 | return String.fromCharCode(n); 238 | } 239 | function type(x) { 240 | x = __as_oak_string(x); 241 | if (x == null) { 242 | return Symbol.for('null'); 243 | } else if (typeof x === 'number') { 244 | // Many discrete APIs check for :int, so we consider all integer 245 | // numbers :int and fall back to :float. This is not an airtight 246 | // solution, but works well enough and the alternative (tagged number 247 | // values/types) have poor perf tradeoffs. 248 | if (Number.isInteger(x)) return Symbol.for('int'); 249 | return Symbol.for('float'); 250 | } else if (__is_oak_string(x)) { 251 | return Symbol.for('string'); 252 | } else if (typeof x === 'boolean') { 253 | return Symbol.for('bool'); 254 | } else if (typeof x === 'symbol') { 255 | if (x === __Oak_Empty) return Symbol.for('empty'); 256 | return Symbol.for('atom'); 257 | } else if (typeof x === 'function') { 258 | return Symbol.for('function'); 259 | } else if (Array.isArray(x)) { 260 | return Symbol.for('list'); 261 | } else if (typeof x === 'object') { 262 | return Symbol.for('object'); 263 | } 264 | throw new Error('type() called on unknown type ' + x.toString()); 265 | } 266 | function len(x) { 267 | if (typeof x === 'string' || __is_oak_string(x) || Array.isArray(x)) { 268 | return x.length; 269 | } else if (typeof x === 'object' && x !== null) { 270 | return Object.getOwnPropertyNames(x).length; 271 | } 272 | throw new Error('len() takes a string or composite value, but got ' + string(x)); 273 | } 274 | function keys(x) { 275 | if (Array.isArray(x)) { 276 | const k = []; 277 | for (let i = 0; i < x.length; i ++) k.push(i); 278 | return k; 279 | } else if (typeof x === 'object' && x !== null) { 280 | return Object.getOwnPropertyNames(x).map(__as_oak_string); 281 | } 282 | throw new Error('keys() takes a composite value, but got ' + string(x).valueOf()); 283 | } 284 | 285 | // OS interfaces 286 | function args() { 287 | if (__Is_Oak_Node) return process.argv.map(__as_oak_string); 288 | return [window.location.href]; 289 | } 290 | function env() { 291 | if (__Is_Oak_Node) { 292 | const e = Object.assign({}, process.env); 293 | for (const key in e) { 294 | e[key] = __as_oak_string(e[key]); 295 | } 296 | return e; 297 | } 298 | return {}; 299 | } 300 | function time() { 301 | return Date.now() / 1000; 302 | } 303 | function nanotime() { 304 | return int(Date.now() * 1000000); 305 | } 306 | function rand() { 307 | return Math.random(); 308 | } 309 | let randomBytes; 310 | function srand(length) { 311 | if (__Is_Oak_Node) { 312 | // lazily import dependency 313 | if (!randomBytes) randomBytes = require('crypto').randomBytes; 314 | return randomBytes(length).toString('latin1'); 315 | } 316 | 317 | const bytes = crypto.getRandomValues(new Uint8Array(length)); 318 | return __as_oak_string(Array.from(bytes).map(b => String.fromCharCode(b)).join('')); 319 | } 320 | function wait(duration, cb) { 321 | setTimeout(cb, duration * 1000); 322 | return null; 323 | } 324 | function exit(code) { 325 | if (__Is_Oak_Node) process.exit(code); 326 | return null; 327 | } 328 | function exec() { 329 | throw new Error('exec() not implemented'); 330 | } 331 | 332 | // I/O 333 | function input() { 334 | throw new Error('input() not implemented'); 335 | } 336 | function print(s) { 337 | s = __as_oak_string(s); 338 | if (__Is_Oak_Node) { 339 | process.stdout.write(string(s).toString()); 340 | } else { 341 | console.log(string(s).toString()); 342 | } 343 | return s.length; 344 | } 345 | function ls() { 346 | throw new Error('ls() not implemented'); 347 | } 348 | function rm() { 349 | throw new Error('rm() not implemented'); 350 | } 351 | function mkdir() { 352 | throw new Error('mkdir() not implemented'); 353 | } 354 | function stat() { 355 | throw new Error('stat() not implemented'); 356 | } 357 | function open() { 358 | throw new Error('open() not implemented'); 359 | } 360 | function close() { 361 | throw new Error('close() not implemented'); 362 | } 363 | function read() { 364 | throw new Error('read() not implemented'); 365 | } 366 | function write() { 367 | throw new Error('write() not implemented'); 368 | } 369 | function listen() { 370 | throw new Error('listen() not implemented'); 371 | } 372 | function req() { 373 | throw new Error('req() not implemented'); 374 | } 375 | 376 | // math 377 | function sin(n) { 378 | return Math.sin(n); 379 | } 380 | function cos(n) { 381 | return Math.cos(n); 382 | } 383 | function tan(n) { 384 | return Math.tan(n); 385 | } 386 | function asin(n) { 387 | return Math.asin(n); 388 | } 389 | function acos(n) { 390 | return Math.acos(n); 391 | } 392 | function atan(n) { 393 | return Math.atan(n); 394 | } 395 | function pow(b, n) { 396 | return Math.pow(b, n); 397 | } 398 | function log(b, n) { 399 | return Math.log(n) / Math.log(b); 400 | } 401 | 402 | // runtime 403 | function ___runtime_lib() { 404 | throw new Error('___runtime_lib() not implemented'); 405 | } 406 | function ___runtime_lib__oak_qm() { 407 | throw new Error('___runtime_lib?() not implemented'); 408 | } 409 | function ___runtime_gc() { 410 | throw new Error('___runtime_gc() not implemented'); 411 | } 412 | function ___runtime_mem() { 413 | throw new Error('___runtime_mem() not implemented'); 414 | } 415 | 416 | // JavaScript interop 417 | function call(target, fn, ...args) { 418 | return target[Symbol.keyFor(fn)](...args); 419 | } 420 | function __oak_js_new(Constructor, ...args) { 421 | return new Constructor(...args); 422 | } 423 | function __oak_js_try(fn) { 424 | try { 425 | return { 426 | type: Symbol.for('ok'), 427 | ok: fn(), 428 | } 429 | } catch (e) { 430 | return { 431 | type: Symbol.for('error'), 432 | error: e, 433 | } 434 | } 435 | } 436 | (__oak_modularize(__Oak_String(``),function _(){return ((BurdPopulation,Burds,Canvas,Ctx,DPI,DrawHeight,DrawWidth,FootprintTrail,Footprints,JumpDistance,JumpDuration,JumpHeight,MaxScale,angle,append,arcBetween,__oak_js_default,draw,drawBurd,drawFootprint,drawShadow,each,ellipse,filter,handleResize,jumpRandomBurd,line,map,math,merge,midJumpBurd,normAngle,println,random,range,slice,sort,squish,startDrawing,startJumping)=>(({println,__oak_js_default,range,slice,map,each,merge,filter,append}=__oak_module_import(__Oak_String(`std`))),(math=__oak_module_import(__Oak_String(`math`))),(sort=__oak_module_import(__Oak_String(`sort`))),(random=__oak_module_import(__Oak_String(`random`))),(DPI=__oak_js_default((window.devicePixelRatio??null),1)),(MaxScale=5),(JumpDuration=0.25),(JumpHeight=0.5),(JumpDistance=12),(FootprintTrail=8),(DrawWidth=(window.innerWidth??null)),(DrawHeight=(window.innerHeight??null)),(Canvas=(document.querySelector)(__Oak_String(`canvas`))),(Ctx=(Canvas.getContext)(__Oak_String(`2d`))),angle=function angle(deg=null){return ((deg/180)*(math.Pi??null))},normAngle=function normAngle(t=null){return ((__as_oak_string(t+(2*(math.Pi??null))))%((2*(math.Pi??null))))},squish=function squish(point=null,center=null,t=null){return ((cx,cy,x,y)=>(([x=null,y=null]=point),([cx=null,cy=null]=center),[__as_oak_string(cx+(((x-cx))*cos(t))),__as_oak_string(cy+(((y-cy))*(__as_oak_string(1+(0.9*(math.abs)(sin(t)))))))]))()},line=function line(start=null,end=null){return ((Ctx.beginPath)(),(Ctx.moveTo)(__oak_acc(start,0),__oak_acc(start,1)),(Ctx.lineTo)(__oak_acc(end,0),__oak_acc(end,1)),(Ctx.stroke)())},ellipse=function ellipse(center=null,rx=null,ry=null){return ((Ctx.beginPath)(),(Ctx.ellipse)(__oak_acc(center,0),__oak_acc(center,1),rx,ry,0,0,(2*(math.Pi??null))),(Ctx.fill)())},arcBetween=function arcBetween(start=null,end=null,radius=null,dir=null,mode=null){return ((arcdist,cx,cy,dist,endAngle,epsilon,startAngle,u,v)=>((epsilon=((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('counterclock'))?1:__oak_eq(__oak_cond,Symbol.for('clock'))?-1:null)(dir)),(dist=(math.hypot)(__oak_acc(start,0),__oak_acc(start,1),__oak_acc(end,0),__oak_acc(end,1))),(arcdist=(math.sqrt)(((radius*radius)-((dist*dist)/4)))),(u=(((__oak_acc(end,0)-__oak_acc(start,0)))/dist)),(v=(((__oak_acc(end,1)-__oak_acc(start,1)))/dist)),(cx=(((__as_oak_string(__oak_acc(start,0)+__oak_acc(end,0)))/2)-((epsilon*arcdist)*v))),(cy=__as_oak_string(((__as_oak_string(__oak_acc(start,1)+__oak_acc(end,1)))/2)+((epsilon*arcdist)*u))),(startAngle=(math.orient)(cx,cy,__oak_acc(start,0),__oak_acc(start,1))),(endAngle=(math.orient)(cx,cy,__oak_acc(end,0),__oak_acc(end,1))),(Ctx.beginPath)(),((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('counterclock'))?(Ctx.arc)(cx,cy,radius,startAngle,endAngle):__oak_eq(__oak_cond,Symbol.for('clock'))?(Ctx.arc)(cx,cy,radius,endAngle,startAngle):null)(dir),((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('fill'))?(Ctx.fill)():__oak_eq(__oak_cond,Symbol.for('stroke'))?(Ctx.stroke)():null)(mode)))()},drawFootprint=function drawFootprint(fp=null){return ((FpRadius,Scale,legSpan)=>((Scale=(((fp.y??null)/DrawHeight)*MaxScale)),(FpRadius=(1*Scale)),(legSpan=(1.5*Scale)),ellipse([__as_oak_string((fp.x??null)+legSpan),(fp.y??null)],FpRadius,(FpRadius/2)),ellipse([((fp.x??null)-legSpan),(fp.y??null)],FpRadius,(FpRadius/2))))()},drawShadow=function drawShadow(burd=null){return ((Scale,dir,shadowRadiusX,shadowRadiusY,x,y,z)=>(({x,y,z,direction:dir=null}=burd),(Scale=((y/DrawHeight)*MaxScale)),(shadowRadiusX=((4*Scale)*(__as_oak_string(1+(0.5*(math.abs)(cos(dir))))))),(shadowRadiusY=(2*Scale)),ellipse([x,(y-z)],shadowRadiusX,shadowRadiusY)))()},drawBurd=function drawBurd(burd=null){return ((Scale,attitude,bodyCenter,bodyHalfLength,clockBodyRadius,color,counterclockBodyRadius,dir,facing,flatBellyRadius,headAtt,headEnd,headLength,headPoint,legLength,legSpan,midBellyRadius,roundBellyRadius,tailPoint,x,y,z)=>(({x,y,z,direction:dir=null,color}=burd),(Scale=((math.scale)(y,0,DrawHeight,0.1,1)*MaxScale)),(facing=((__oak_cond)=>__oak_eq(__oak_cond,(dirangle((360-70))))?Symbol.for('east'):__oak_eq(__oak_cond,(dir__oak_eq(__oak_cond,Symbol.for('east'))?(-1*Scale):__oak_eq(__oak_cond,Symbol.for('west'))?(1*Scale):0)(facing)),(y-(11*Scale))]),(bodyHalfLength=(8*Scale)),(attitude=angle(30)),(headPoint=squish((math.bearing)(__oak_acc(bodyCenter,0),__oak_acc(bodyCenter,1),bodyHalfLength,-attitude),bodyCenter,dir)),(tailPoint=squish((math.bearing)(__oak_acc(bodyCenter,0),__oak_acc(bodyCenter,1),bodyHalfLength,__as_oak_string(-attitude+(math.Pi??null))),bodyCenter,dir)),(headLength=(8*Scale)),(headAtt=((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('east'))?angle(-30):__oak_eq(__oak_cond,Symbol.for('west'))?angle(__as_oak_string(180+30)):((__oak_cond)=>__oak_eq(__oak_cond,(dirangle(270)))?angle(-10):angle(__as_oak_string(180+10)))(true))(facing)),(headEnd=(math.bearing)(__oak_acc(headPoint,0),__oak_acc(headPoint,1),headLength,-headAtt)),(roundBellyRadius=(8.75*Scale)),(flatBellyRadius=(25*Scale)),(midBellyRadius=(10.5*Scale)),([clockBodyRadius=null,counterclockBodyRadius=null]=((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('east'))?[flatBellyRadius,roundBellyRadius]:__oak_eq(__oak_cond,Symbol.for('west'))?[roundBellyRadius,flatBellyRadius]:[midBellyRadius,midBellyRadius])(facing)),Symbol.for('legs'),(((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(lineWidth,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.lineWidth):(__oak_assgn_tgt.lineWidth)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),(Scale/2)),line([__as_oak_string(x+legSpan),y],[__as_oak_string(x+legSpan),(y-legLength)]),line([(x-legSpan),y],[(x-legSpan),(y-legLength)])),Symbol.for('body'),(((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(fillStyle,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.fillStyle):(__oak_assgn_tgt.fillStyle)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),color),arcBetween(headPoint,tailPoint,clockBodyRadius,Symbol.for('clock'),Symbol.for('fill')),arcBetween(headPoint,tailPoint,counterclockBodyRadius,Symbol.for('counterclock'),Symbol.for('fill')),arcBetween(headPoint,tailPoint,clockBodyRadius,Symbol.for('clock'),Symbol.for('stroke')),arcBetween(headPoint,tailPoint,counterclockBodyRadius,Symbol.for('counterclock'),Symbol.for('stroke'))),Symbol.for('head'),(line(headPoint,headEnd))))()},midJumpBurd=function midJumpBurd(burd=null){return ((Scale,c,color,destX,destY,dir,jumpY,jumped,movingX,movingY,t,x,y)=>(({x,y,direction:dir=null,jumped,color}=burd),(Scale=((y/DrawHeight)*MaxScale)),(t=(math.scale)(time(),jumped,__as_oak_string(jumped+JumpDuration))),([destX=null,destY=null]=(math.bearing)(x,y,(JumpDistance*Scale),-dir)),(c=((((4*JumpHeight)*Scale)/JumpDuration)/JumpDuration)),(jumpY=((c*t)*((t-1)))),(movingX=(math.scale)(t,0,1,x,destX)),(movingY=__as_oak_string((math.scale)(t,0,1,y,destY)+jumpY)),({x:movingX,y:movingY,z:jumpY,direction:dir,color})))()},draw=function draw(){return ((JumpedBurds)=>((Ctx.setTransform)(DPI,0,0,DPI,0,0),(Ctx.clearRect)(0,0,(Canvas.width??null),(Canvas.height??null)),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(lineCap,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.lineCap):(__oak_assgn_tgt.lineCap)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),__Oak_String(`round`)),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(lineJoin,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.lineJoin):(__oak_assgn_tgt.lineJoin)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),__Oak_String(`round`)),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(strokeStyle,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.strokeStyle):(__oak_assgn_tgt.strokeStyle)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),__Oak_String(`#000000`)),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(fillStyle,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.fillStyle):(__oak_assgn_tgt.fillStyle)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),__Oak_String(`#cccccc`)),each(Footprints,drawFootprint),(JumpedBurds=map(Burds,function _(burd=null){return ((__oak_cond)=>__oak_eq(__oak_cond,null)?burd:midJumpBurd(burd))((burd.jumped??null))})),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(fillStyle,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.fillStyle):(__oak_assgn_tgt.fillStyle)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Ctx),__Oak_String(`rgba(0, 0, 0, 0.2)`)),each(JumpedBurds,drawShadow),each((sort.sort)(JumpedBurds,function _(b=null){return ((b.y??null)-(b.z??null))}),drawBurd)))()},handleResize=function handleResize(){return ((DrawWidth=(window.innerWidth??null)),(DrawHeight=(window.innerHeight??null)),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(width,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.width):(__oak_assgn_tgt.width)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Canvas),int((DrawWidth*DPI))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(height,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.height):(__oak_assgn_tgt.height)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(Canvas),int((DrawHeight*DPI))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(width,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.width):(__oak_assgn_tgt.width)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string((Canvas.style??null)),__as_oak_string(string(DrawWidth)+__Oak_String(`px`))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(height,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.height):(__oak_assgn_tgt.height)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string((Canvas.style??null)),__as_oak_string(string(DrawHeight)+__Oak_String(`px`))),draw())},(BurdPopulation=(math.max)((math.min)((((DrawWidth*DrawHeight)/50000)),50),6)),(Burds=map(range(BurdPopulation),function _(n=null){return ({x:(random.number)(5,(DrawWidth-5)),y:(random.number)(5,(DrawHeight-5)),z:0,direction:(random.number)(0,(2*(math.Pi??null))),color:(random.choice)([__Oak_String(`#000000`),__Oak_String(`#f0eff1`)])})})),(Footprints=[]),jumpRandomBurd=function jumpRandomBurd(){return ((burd)=>(((__oak_cond)=>__oak_eq(__oak_cond,true)?((MaxFootprints)=>(((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(jumped,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.jumped):(__oak_assgn_tgt.jumped)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(burd),time()),__oak_push(Footprints,({x:(burd.x??null),y:(burd.y??null)})),(MaxFootprints=int((BurdPopulation*FootprintTrail))),((__oak_cond)=>__oak_eq(__oak_cond,true)?((Footprints=slice(Footprints,(len(Footprints)-MaxFootprints)))):null)((len(Footprints)>MaxFootprints)),wait(JumpDuration,function _(){return ((Scale,direction,x,y)=>((Scale=(((burd.y??null)/DrawHeight)*MaxScale)),([x=null,y=null]=(math.bearing)((burd.x??null),(burd.y??null),(JumpDistance*Scale),-(burd.direction??null))),(direction=normAngle(((__oak_cond)=>__oak_eq(__oak_cond,(x<50))?(math.orient)(x,-y,(DrawWidth/2),(-DrawHeight/2)):__oak_eq(__oak_cond,(x>(DrawWidth-50)))?(math.orient)(x,-y,(DrawWidth/2),(-DrawHeight/2)):__oak_eq(__oak_cond,(y<50))?(math.orient)(x,-y,(DrawWidth/2),(-DrawHeight/2)):__oak_eq(__oak_cond,(y>(DrawHeight-50)))?(math.orient)(x,-y,(DrawWidth/2),(-DrawHeight/2)):__as_oak_string((burd.direction??null)+(random.number)(angle(-30),angle(30))))(true))),merge(burd,({x,y,direction,jumped:null}))))()})))():null)(!__oak_eq(null,(burd=(random.choice)(filter(Burds,function _(b=null){return __oak_eq((b.jumped??null),null)})))))))()},startDrawing=function startDrawing(){return (draw(),requestAnimationFrame(startDrawing))},startJumping=function startJumping(){return (wait((1/BurdPopulation),function _(){return (jumpRandomBurd(),startJumping())}))},handleResize(),(window.addEventListener)(__Oak_String(`resize`),handleResize),((document.querySelector)(__Oak_String(`.hideButton`)).addEventListener)(__Oak_String(`click`),function _(){return (((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign(display,__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt.display):(__oak_assgn_tgt.display)=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(((document.querySelector)(__Oak_String(`#attribution`)).style??null)),__Oak_String(`none`)))}),startDrawing(),startJumping(),({BurdPopulation,Burds,Canvas,Ctx,DPI,DrawHeight,DrawWidth,FootprintTrail,Footprints,JumpDistance,JumpDuration,JumpHeight,MaxScale,angle,append,arcBetween,__oak_js_default,draw,drawBurd,drawFootprint,drawShadow,each,ellipse,filter,handleResize,jumpRandomBurd,line,map,math,merge,midJumpBurd,normAngle,println,random,range,slice,sort,squish,startDrawing,startJumping})))()}),__oak_modularize(__Oak_String(`math`),function _(){return ((E,Pi,abs,bearing,clamp,__oak_js_default,hypot,map,max,mean,median,min,orient,prod,reduce,round,scale,sign,sort,sqrt,stddev,sum)=>(({__oak_js_default,map,reduce}=__oak_module_import(__Oak_String(`std`))),({sort}=__oak_module_import(__Oak_String(`sort`))),(Pi=3.141592653589793),(E=2.718281828459045),sign=function sign(n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?1:-1)((n>=0))},abs=function abs(n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?n:-n)((n>=0))},sqrt=function sqrt(n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?pow(n,0.5):null)((n>=0))},hypot=function hypot(x0=null,y0=null,x1=null,y1=null){return (((__oak_cond)=>__oak_eq(__oak_cond,true)?(x1=(y1=0)):null)((__oak_left=>__oak_left===false?false:__oak_and(__oak_left,__oak_eq(y1,null)))(__oak_eq(x1,null))),sqrt(__as_oak_string((((x0-x1))*((x0-x1)))+(((y0-y1))*((y0-y1))))))},scale=function scale(x=null,a=null,b=null,c=null,d=null){return ((normed)=>((normed=(((x-a))/((b-a)))),((__oak_cond)=>__oak_eq(__oak_cond,(__oak_left=>__oak_left===false?false:__oak_and(__oak_left,__oak_eq(d,null)))(__oak_eq(c,null)))?normed:__as_oak_string((((d-c))*normed)+c))(true)))()},bearing=function bearing(x=null,y=null,d=null,t=null){return [__as_oak_string(x+(d*cos(t))),__as_oak_string(y+(d*sin(t)))]},orient=function orient(x0=null,y0=null,x1=null,y1=null){return ((x,y)=>(([x=null,y=null]=((__oak_cond)=>__oak_eq(__oak_cond,true)?[x0,y0]:[(x1-x0),(y1-y0)])((__oak_left=>__oak_left===false?false:__oak_and(__oak_left,__oak_eq(y1,null)))(__oak_eq(x1,null)))),((__oak_cond)=>__oak_eq(__oak_cond,(x>0))?(2*atan((y/(__as_oak_string(hypot(x,y)+x))))):__oak_eq(__oak_cond,(__oak_left=>__oak_left===false?false:__oak_and(__oak_left,!__oak_eq(y,0)))((x<=0)))?(2*atan((((hypot(x,y)-x))/y))):__oak_eq(__oak_cond,(__oak_left=>__oak_left===false?false:__oak_and(__oak_left,__oak_eq(y,0)))((x<0)))?Pi:null)(true)))()},sum=function sum(...xs){return reduce(xs,0,function _(a=null,b=null){return __as_oak_string(a+b)})},prod=function prod(...xs){return reduce(xs,1,function _(a=null,b=null){return (a*b)})},min=function min(...xs){return reduce(xs,__oak_acc(xs,0),function _(acc=null,n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?n:acc)((n__oak_eq(__oak_cond,true)?n:acc)((n>acc))})},clamp=function clamp(x=null,a=null,b=null){return ((__oak_cond)=>__oak_eq(__oak_cond,(xb))?b:x)(true)},mean=function mean(xs=null){return ((__oak_cond)=>__oak_eq(__oak_cond,0)?null:(sum(...xs)/len(xs)))(len(xs))},median=function median(xs=null){return ((count,half)=>((xs=sort(xs)),(count=len(xs)),(half=int((count/2))),((__oak_cond)=>__oak_eq(__oak_cond,count)?null:__oak_eq(__oak_cond,(count%2))?((__as_oak_string(__oak_acc(xs,__oak_obj_key(((half-1))))+__oak_acc(xs,__oak_obj_key((half)))))/2):__oak_acc(xs,__oak_obj_key((half))))(0)))()},stddev=function stddev(xs=null){let xmean;return ((__oak_cond)=>__oak_eq(__oak_cond,true)?(sqrt(mean(map(xs,function _(x=null){return pow((xmean-x),2)})))):null)(!__oak_eq(null,(xmean=mean(xs))))},round=function round(n=null,decimals=null){return ((decimals=__oak_js_default(int(decimals),0)),((__oak_cond)=>__oak_eq(__oak_cond,true)?n:((order)=>((order=pow(10,decimals)),((__oak_cond)=>__oak_eq(__oak_cond,true)?(int(__as_oak_string((n*order)+0.5))/order):(-int(__as_oak_string((-n*order)+0.5))/order))((n>=0))))())((decimals<0)))},({E,Pi,abs,bearing,clamp,__oak_js_default,hypot,map,max,mean,median,min,orient,prod,reduce,round,scale,sign,sort,sqrt,stddev,sum})))()}),__oak_modularize(__Oak_String(`random`),function _(){return ((boolean,choice,integer,number)=>(boolean=function boolean(){return (rand()>0.5)},integer=function integer(min=null,max=null){return int(number(int(min),int(max)))},number=function number(min=null,max=null){return (((__oak_cond)=>__oak_eq(__oak_cond,true)?([min=null,max=null]=[0,min]):null)(__oak_eq(max,null)),__as_oak_string(min+(rand()*((max-min)))))},choice=function choice(list=null){return __oak_acc(list,__oak_obj_key((integer(0,len(list)))))},({boolean,choice,integer,number})))()}),__oak_modularize(__Oak_String(`sort`),function _(){return ((clone,__oak_js_default,id,map,sort,sort__oak_exclam)=>(({__oak_js_default,identity:id=null,map,clone}=__oak_module_import(__Oak_String(`std`))),sort__oak_exclam=function sort__oak_exclam(xs=null,pred=null){return ((partition,quicksort,vpred)=>((pred=__oak_js_default(pred,id)),(vpred=map(xs,pred)),partition=function partition(xs=null,lo=null,hi=null){return ((lsub,pivot,rsub,sub)=>((pivot=__oak_acc(vpred,__oak_obj_key((lo)))),lsub=function lsub(i=null){return ((__oak_trampolined_lsub)=>((__oak_trampolined_lsub=function _(i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?__oak_trampoline(__oak_trampolined_lsub,__as_oak_string(i+1)):i)((__oak_acc(vpred,__oak_obj_key((i)))((__oak_trampolined_rsub=function _(j=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?__oak_trampoline(__oak_trampolined_rsub,(j-1)):j)((__oak_acc(vpred,__oak_obj_key((j)))>pivot))}),__oak_resolve_trampoline(__oak_trampolined_rsub,j)))()},sub=function sub(i=null,j=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null,j=null){return ((i=lsub(i)),(j=rsub(j)),((__oak_cond)=>__oak_eq(__oak_cond,false)?j:((tmp,tmpPred)=>((tmp=__oak_acc(xs,__oak_obj_key((i)))),(tmpPred=__oak_acc(vpred,__oak_obj_key((i)))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((i),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((i))]):(__oak_assgn_tgt[__oak_obj_key((i))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(xs),__oak_acc(xs,__oak_obj_key((j)))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((j),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((j))]):(__oak_assgn_tgt[__oak_obj_key((j))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(xs),tmp),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((i),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((i))]):(__oak_assgn_tgt[__oak_obj_key((i))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(vpred),__oak_acc(vpred,__oak_obj_key((j)))),((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((j),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((j))]):(__oak_assgn_tgt[__oak_obj_key((j))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(vpred),tmpPred),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1),(j-1))))())((i((__oak_trampolined_quicksort=function _(xs=null,lo=null,hi=null){return ((__oak_cond)=>__oak_eq(__oak_cond,0)?xs:__oak_eq(__oak_cond,1)?xs:((__oak_cond)=>__oak_eq(__oak_cond,false)?xs:((p)=>((p=partition(xs,lo,hi)),quicksort(xs,lo,p),__oak_trampoline(__oak_trampolined_quicksort,xs,__as_oak_string(p+1),hi)))())((lo(identity=function identity(x=null){return x},_baseIterator=function _baseIterator(v=null){return ((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('string'))?__Oak_String(``):__oak_eq(__oak_cond,Symbol.for('list'))?[]:__oak_eq(__oak_cond,Symbol.for('object'))?({}):null)(type(v))},_asPredicate=function _asPredicate(pred=null){return ((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('atom'))?((prop)=>((prop=string(pred)),function _(x=null){return __oak_acc(x,__oak_obj_key((prop)))}))():__oak_eq(__oak_cond,Symbol.for('string'))?function _(x=null){return __oak_acc(x,__oak_obj_key((pred)))}:__oak_eq(__oak_cond,Symbol.for('int'))?function _(x=null){return __oak_acc(x,__oak_obj_key((pred)))}:pred)(type(pred))},__oak_js_default=function __oak_js_default(x=null,base=null){return ((__oak_cond)=>__oak_eq(__oak_cond,null)?base:x)(x)},(_nToH=__Oak_String(`0123456789abcdef`)),toHex=function toHex(n=null){return ((sub)=>(sub=function sub(p=null,acc=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(p=null,acc=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?__as_oak_string(__oak_acc(_nToH,__oak_obj_key((p)))+acc):__oak_trampoline(__oak_trampolined_sub,int((p/16)),__as_oak_string(__oak_acc(_nToH,__oak_obj_key(((p%16))))+acc)))((p<16))}),__oak_resolve_trampoline(__oak_trampolined_sub,p,acc)))()},sub(int(n),__Oak_String(``))))()},(_hToN=({0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15})),fromHex=function fromHex(s=null){return ((sub)=>(sub=function sub(i=null,acc=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null,acc=null){let next;return ((__oak_cond)=>__oak_eq(__oak_cond,__oak_eq(i,len(s)))?acc:__oak_eq(__oak_cond,!__oak_eq(null,(next=__oak_acc(_hToN,__oak_obj_key((__oak_acc(s,__oak_obj_key((i)))))))))?__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1),__as_oak_string((acc*16)+next)):null)(true)}),__oak_resolve_trampoline(__oak_trampolined_sub,i,acc)))()},sub(0,0)))()},clamp=function clamp(min=null,max=null,n=null,m=null){return ((n=((__oak_cond)=>__oak_eq(__oak_cond,true)?min:n)((n__oak_eq(__oak_cond,true)?min:m)((m__oak_eq(__oak_cond,true)?max:m)((m>max))),(n=((__oak_cond)=>__oak_eq(__oak_cond,true)?m:n)((n>m))),[n,m])},slice=function slice(xs=null,min=null,max=null){return ((sub)=>((min=__oak_js_default(min,0)),(max=__oak_js_default(max,len(xs))),([min=null,max=null]=clamp(0,len(xs),min,max)),sub=function sub(acc=null,i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,max)?acc:__oak_trampoline(__oak_trampolined_sub,__oak_push(acc,__oak_acc(xs,__oak_obj_key((i)))),__as_oak_string(i+1)))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub(_baseIterator(xs),min)))()},clone=function clone(x=null){return ((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('string'))?__as_oak_string(__Oak_String(``)+x):__oak_eq(__oak_cond,Symbol.for('list'))?slice(x):__oak_eq(__oak_cond,Symbol.for('object'))?reduce(keys(x),({}),function _(acc=null,key=null){return ((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((key),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((key))]):(__oak_assgn_tgt[__oak_obj_key((key))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(acc),__oak_acc(x,__oak_obj_key((key))))}):x)(type(x))},range=function range(start=null,end=null,step=null){return ((step=__oak_js_default(step,1)),((__oak_cond)=>__oak_eq(__oak_cond,true)?([start=null,end=null]=[0,start]):null)(__oak_eq(end,null)),((__oak_cond)=>__oak_eq(__oak_cond,0)?[]:((list,sub)=>((list=[]),((__oak_cond)=>__oak_eq(__oak_cond,true)?sub=function sub(n=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?(__oak_push(list,n),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(n+step))):list)((n((__oak_trampolined_sub=function _(n=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?(__oak_push(list,n),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(n+step))):list)((n>end))}),__oak_resolve_trampoline(__oak_trampolined_sub,n)))()})((step>0)),sub(start)))())(step))},reverse=function reverse(xs=null){return ((sub)=>(sub=function sub(acc=null,i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?acc:__oak_trampoline(__oak_trampolined_sub,__oak_push(acc,__oak_acc(xs,__oak_obj_key((i)))),(i-1)))((i<0))}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub(_baseIterator(xs),(len(xs)-1))))()},map=function map(xs=null,f=null){return ((sub)=>((f=_asPredicate(f)),sub=function sub(acc=null,i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?acc:__oak_trampoline(__oak_trampolined_sub,__oak_push(acc,f(__oak_acc(xs,__oak_obj_key((i))),i)),__as_oak_string(i+1)))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub(_baseIterator(xs),0)))()},each=function each(xs=null,f=null){return ((sub)=>(sub=function sub(i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?null:(f(__oak_acc(xs,__oak_obj_key((i))),i),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1))))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,i)))()},sub(0)))()},filter=function filter(xs=null,f=null){return ((sub)=>((f=_asPredicate(f)),sub=function sub(acc=null,i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?acc:((x)=>(((__oak_cond)=>__oak_eq(__oak_cond,true)?__oak_push(acc,x):null)(f((x=__oak_acc(xs,__oak_obj_key((i)))),i)),__oak_trampoline(__oak_trampolined_sub,acc,__as_oak_string(i+1))))())(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub(_baseIterator(xs),0)))()},reduce=function reduce(xs=null,seed=null,f=null){return ((sub)=>(sub=function sub(acc=null,i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?acc:__oak_trampoline(__oak_trampolined_sub,f(acc,__oak_acc(xs,__oak_obj_key((i))),i),__as_oak_string(i+1)))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub(seed,0)))()},flatten=function flatten(xs=null){return reduce(xs,[],append)},compact=function compact(xs=null){return filter(xs,function _(x=null){return !__oak_eq(x,null)})},some=function some(xs=null,pred=null){return ((pred=__oak_js_default(pred,identity)),reduce(xs,false,function _(acc=null,x=null,i=null){return (__oak_left=>__oak_left===true?true:__oak_or(__oak_left,pred(x,i)))(acc)}))},every=function every(xs=null,pred=null){return ((pred=__oak_js_default(pred,identity)),reduce(xs,true,function _(acc=null,x=null,i=null){return (__oak_left=>__oak_left===false?false:__oak_and(__oak_left,pred(x,i)))(acc)}))},append=function append(xs=null,ys=null){return reduce(ys,xs,function _(zs=null,y=null){return __oak_push(zs,y)})},join=function join(xs=null,ys=null){return append(clone(xs),ys)},zip=function zip(xs=null,ys=null,zipper=null){return ((max,sub)=>((zipper=__oak_js_default(zipper,function _(x=null,y=null){return [x,y]})),(max=((__oak_cond)=>__oak_eq(__oak_cond,true)?len(xs):len(ys))((len(xs)((__oak_trampolined_sub=function _(acc=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,max)?acc:__oak_trampoline(__oak_trampolined_sub,__oak_push(acc,zipper(__oak_acc(xs,__oak_obj_key((i))),__oak_acc(ys,__oak_obj_key((i))),i)),__as_oak_string(i+1)))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,acc,i)))()},sub([],0)))()},partition=function partition(xs=null,by=null){return ((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('int'))?reduce(xs,[],function _(acc=null,x=null,i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,0)?__oak_push(acc,[x]):(__oak_push(__oak_acc(acc,__oak_obj_key(((len(acc)-1)))),x),acc))((i%by))}):__oak_eq(__oak_cond,Symbol.for('function'))?((last)=>((last=function _(){return null}),reduce(xs,[],function _(acc=null,x=null){return ((__oak_js_this)=>(((__oak_cond)=>__oak_eq(__oak_cond,last)?__oak_push(__oak_acc(acc,__oak_obj_key(((len(acc)-1)))),x):__oak_push(acc,[x]))((__oak_js_this=by(x))),(last=__oak_js_this),acc))()})))():null)(type(by))},uniq=function uniq(xs=null,pred=null){return ((last,sub,ys)=>((pred=__oak_js_default(pred,identity)),(ys=_baseIterator(xs)),(last=function _(){return null}),sub=function sub(i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null){let p;let x;return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?ys:((__oak_cond)=>__oak_eq(__oak_cond,last)?__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1)):(__oak_push(ys,x),(last=p),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1))))((p=pred((x=__oak_acc(xs,__oak_obj_key((i))))))))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,i)))()},sub(0)))()},first=function first(xs=null){return __oak_acc(xs,0)},last=function last(xs=null){return __oak_acc(xs,__oak_obj_key(((len(xs)-1))))},take=function take(xs=null,n=null){return slice(xs,0,n)},takeLast=function takeLast(xs=null,n=null){return slice(xs,(len(xs)-n))},find=function find(xs=null,pred=null){return ((sub)=>(sub=function sub(i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?-1:((__oak_cond)=>__oak_eq(__oak_cond,true)?i:__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1)))(pred(__oak_acc(xs,__oak_obj_key((i))))))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,i)))()},sub(0)))()},indexOf=function indexOf(xs=null,x=null){return ((sub)=>(sub=function sub(i=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(i=null){return ((__oak_cond)=>__oak_eq(__oak_cond,len(xs))?-1:((__oak_cond)=>__oak_eq(__oak_cond,x)?i:__oak_trampoline(__oak_trampolined_sub,__as_oak_string(i+1)))(__oak_acc(xs,__oak_obj_key((i)))))(i)}),__oak_resolve_trampoline(__oak_trampolined_sub,i)))()},sub(0)))()},contains__oak_qm=function contains__oak_qm(xs=null,x=null){return (indexOf(xs,x)>-1)},values=function values(obj=null){return map(keys(obj),function _(key=null){return __oak_acc(obj,__oak_obj_key((key)))})},entries=function entries(obj=null){return map(keys(obj),function _(key=null){return [key,__oak_acc(obj,__oak_obj_key((key)))]})},merge=function merge(...os){return ((__oak_cond)=>__oak_eq(__oak_cond,0)?null:reduce(os,__oak_acc(os,0),function _(acc=null,o=null){return (reduce(keys(o),acc,function _(root=null,k=null){return ((__oak_assgn_tgt,__oak_assgn_val)=>(__is_oak_string(__oak_assgn_tgt)?__oak_assgn_tgt.assign((k),__oak_assgn_val):__oak_assgn_val===__Oak_Empty?delete (__oak_assgn_tgt[__oak_obj_key((k))]):(__oak_assgn_tgt[__oak_obj_key((k))])=__oak_assgn_val,__oak_assgn_tgt))(__as_oak_string(root),__oak_acc(o,__oak_obj_key((k))))}))}))(len(os))},once=function once(f=null){return ((called__oak_qm)=>((called__oak_qm=false),function _(...args){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?((called__oak_qm=true),f(...args)):null)(!called__oak_qm)}))()},loop=function loop(max=null,f=null){return ((breaker,broken,sub)=>(((__oak_cond)=>__oak_eq(__oak_cond,true)?([max=null,f=null]=[-1,max]):null)(__oak_eq(f,null)),(max=__oak_js_default(max,-1)),(broken=false),breaker=function breaker(){return (broken=true)},sub=function sub(count=null){return ((__oak_trampolined_sub)=>((__oak_trampolined_sub=function _(count=null){return ((__oak_cond)=>__oak_eq(__oak_cond,true)?(((__oak_cond)=>__oak_eq(__oak_cond,true)?(f(count,breaker),__oak_trampoline(__oak_trampolined_sub,__as_oak_string(count+1))):null)(!broken)):null)(!__oak_eq(count,max))}),__oak_resolve_trampoline(__oak_trampolined_sub,count)))()},sub(0)))()},debounce=function debounce(duration=null,firstCall=null,f=null){return ((dargs,debounced,target,waiting__oak_qm)=>(((__oak_cond)=>__oak_eq(__oak_cond,true)?([firstCall=null,f=null]=[Symbol.for('trailing'),firstCall]):null)(__oak_eq(f,null)),(dargs=null),(waiting__oak_qm=false),(target=(time()-duration)),debounced=function debounced(...args){return ((tcall)=>((tcall=time()),(dargs=args),((__oak_cond)=>__oak_eq(__oak_cond,true)?((__oak_cond)=>__oak_eq(__oak_cond,true)?((target=__as_oak_string(tcall+duration)),((__oak_cond)=>__oak_eq(__oak_cond,Symbol.for('leading'))?f(...dargs):__oak_eq(__oak_cond,Symbol.for('trailing'))?((waiting__oak_qm=true),wait((target-time()),function _(){return ((waiting__oak_qm=false),f(...dargs))})):null)(firstCall)):((timeout)=>((waiting__oak_qm=true),(timeout=(target-tcall)),(target=__as_oak_string(target+duration)),wait(timeout,function _(){return ((waiting__oak_qm=false),f(...dargs))})))())((target<=tcall)):null)(!waiting__oak_qm)))()}))()},println=function println(...xs){return ((__oak_cond)=>__oak_eq(__oak_cond,0)?print(__Oak_String(` 437 | `)):((out)=>((out=reduce(slice(xs,1),string(__oak_acc(xs,0)),function _(acc=null,x=null){return (__as_oak_string(__as_oak_string(acc+__Oak_String(` `))+string(x)))})),print(__as_oak_string(out+__Oak_String(` 438 | `)))))())(len(xs))},({_asPredicate,_baseIterator,_hToN,_nToH,append,clamp,clone,compact,contains__oak_qm,debounce,__oak_js_default,each,entries,every,filter,find,first,flatten,fromHex,identity,indexOf,join,last,loop,map,merge,once,partition,println,range,reduce,reverse,slice,some,take,takeLast,toHex,uniq,values,zip})))()}),(__Oak_Import_Aliases=({})),__oak_module_import(__Oak_String(``))) -------------------------------------------------------------------------------- /img/screenshot-full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thesephist/burds/5ada8b58d87efdd049c9210aedc0fd392a067c2a/img/screenshot-full.jpg -------------------------------------------------------------------------------- /img/screenshot-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thesephist/burds/5ada8b58d87efdd049c9210aedc0fd392a067c2a/img/screenshot-thumb.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | burds! 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 101 |
102 |

burds!

103 |

web experiment by Linus

104 |

inspiration from Loek Vugs

105 |

open source on GitHub

106 | 107 |
108 | 109 | 110 | --------------------------------------------------------------------------------