├── .gitignore ├── .DS_Store ├── assets ├── oh.mp3 ├── clap.mp3 ├── sprite.png ├── title.png ├── readme │ ├── game.png │ ├── video.jpg │ └── interface.png └── sprite org.png ├── public ├── assets │ ├── clap.mp3 │ ├── oh.mp3 │ ├── sprite.png │ ├── title.png │ ├── readme │ │ ├── game.png │ │ ├── video.jpg │ │ └── interface.png │ └── sprite org.png ├── css │ ├── style.css.map │ └── style.css ├── index.html └── datasets │ ├── knips-weee.json │ ├── default_40-40.json │ └── default.json ├── js ├── game │ ├── Moon.js │ ├── Backdrop.js │ ├── Number.js │ ├── BigNumber.js │ ├── Mountain.js │ ├── Title.js │ ├── Section.js │ ├── ScoreBoard.js │ ├── Icon.js │ ├── Player.js │ ├── Obstacle.js │ └── Game.js ├── main.js └── ai │ ├── KNN.js │ ├── Microphone.js │ └── AudioClassifier.js ├── package.json ├── html └── index.html ├── style └── main.styl └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/.DS_Store -------------------------------------------------------------------------------- /assets/oh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/oh.mp3 -------------------------------------------------------------------------------- /assets/clap.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/clap.mp3 -------------------------------------------------------------------------------- /assets/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/sprite.png -------------------------------------------------------------------------------- /assets/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/title.png -------------------------------------------------------------------------------- /assets/readme/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/readme/game.png -------------------------------------------------------------------------------- /assets/sprite org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/sprite org.png -------------------------------------------------------------------------------- /public/assets/clap.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/clap.mp3 -------------------------------------------------------------------------------- /public/assets/oh.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/oh.mp3 -------------------------------------------------------------------------------- /assets/readme/video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/readme/video.jpg -------------------------------------------------------------------------------- /public/assets/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/sprite.png -------------------------------------------------------------------------------- /public/assets/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/title.png -------------------------------------------------------------------------------- /assets/readme/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/assets/readme/interface.png -------------------------------------------------------------------------------- /public/assets/readme/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/readme/game.png -------------------------------------------------------------------------------- /public/assets/sprite org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/sprite org.png -------------------------------------------------------------------------------- /public/assets/readme/video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/readme/video.jpg -------------------------------------------------------------------------------- /public/assets/readme/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lassse/sound-controlled-intergalactic-teddy/HEAD/public/assets/readme/interface.png -------------------------------------------------------------------------------- /js/game/Moon.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main.js' 3 | 4 | class Moon { 5 | constructor() { 6 | this.image = Q.spriteImage 7 | this.x = 10 8 | this.y = 10 9 | this.width = 90 10 | this.height = 90 11 | 12 | this.sourceX = 100 13 | this.sourceY = 200 14 | this.sourceWidth = 90 15 | this.sourceHeight = 90 16 | 17 | } 18 | 19 | render(context) { 20 | 21 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 22 | } 23 | } 24 | 25 | export default Moon -------------------------------------------------------------------------------- /js/game/Backdrop.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main.js' 3 | 4 | class Backdrop { 5 | constructor(x) { 6 | this.image = Q.spriteImage 7 | this.x = x 8 | this.y = 0 9 | this.width = 1400 10 | this.height = 572 11 | 12 | this.sourceX = 428 13 | this.sourceY = 196 14 | this.sourceWidth = 1400 15 | this.sourceHeight = 572 16 | } 17 | 18 | render(context) { 19 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 20 | } 21 | } 22 | 23 | export default Backdrop -------------------------------------------------------------------------------- /js/game/Number.js: -------------------------------------------------------------------------------- 1 | import Q from './../main.js' 2 | 3 | class Number { 4 | constructor() { 5 | this.image = Q.spriteImage 6 | this.value = null 7 | 8 | this.width = 20 9 | this.height = 28 10 | 11 | this.x = 0 12 | this.y = 0 13 | 14 | this.sourceX = 116 15 | this.sourceY = 1500 16 | this.sourceWidth = 20 17 | this.sourceHeight = 28 18 | } 19 | 20 | set(value) { 21 | this.value = value 22 | } 23 | 24 | clear() { 25 | this.value = 0 26 | } 27 | 28 | render(context) { 29 | context.drawImage(this.image, this.sourceX + (this.sourceWidth * (this.value)), this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 30 | } 31 | } 32 | 33 | export default Number -------------------------------------------------------------------------------- /js/game/BigNumber.js: -------------------------------------------------------------------------------- 1 | import Q from './../main.js' 2 | 3 | class BigNumber { 4 | constructor() { 5 | this.image = Q.spriteImage 6 | this.value = null 7 | 8 | this.width = 80 9 | this.height = 116 10 | 11 | this.x = 0 12 | this.y = 0 13 | 14 | this.sourceX = 630 15 | this.sourceY = 1980 16 | this.sourceWidth = 80 17 | this.sourceHeight = 116 18 | this.show = true 19 | } 20 | 21 | set(value) { 22 | this.show = true 23 | this.value = value 24 | } 25 | 26 | clear() { 27 | this.value = 0 28 | this.show = false 29 | } 30 | 31 | render(context) { 32 | if (this.show) { 33 | context.drawImage(this.image, this.sourceX + (this.sourceWidth * (this.value)), this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 34 | } 35 | } 36 | } 37 | 38 | export default BigNumber -------------------------------------------------------------------------------- /js/game/Mountain.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main.js' 3 | 4 | class Mountain { 5 | constructor(index) { 6 | this.image = Q.spriteImage 7 | 8 | if (index === 0) { 9 | this.x = 0 10 | this.y = 60 11 | this.width = 1400 12 | this.height = 453 13 | this.sourceX = 428 14 | this.sourceY = 768 15 | this.sourceWidth = 1400 16 | this.sourceHeight = 453 17 | }else if (index === 1) { 18 | this.x = 0 19 | this.y = 200 20 | this.width = 1400 21 | this.height = 342 22 | this.sourceX = 428 23 | this.sourceY = 1220 24 | this.sourceWidth = 1400 25 | this.sourceHeight = 342 26 | }else if (index === 2) { 27 | this.x = 0 28 | this.y = 368 29 | this.width = 1400 30 | this.height = 100 31 | this.sourceX = 428 32 | this.sourceY = 1560 33 | this.sourceWidth = 1400 34 | this.sourceHeight = 100 35 | } 36 | 37 | } 38 | 39 | render(context) { 40 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 41 | } 42 | } 43 | 44 | export default Mountain -------------------------------------------------------------------------------- /public/css/style.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../style/main.styl"],"names":[],"mappings":"AAAA;AAAM;EACL,QAAQ,EAAR;EACA,OAAO,KAAP;EACA,QAAQ,KAAR;EACA,SAAS,KAAT;EACA,iBAAiB,OAAjB;EACA,aAAa,OAAb;EACA,YAAY,KAAZ;;AAID;EACC,UAAS,SAAT;EACA,YAAW,KAAX;EACA,OAAO,MAAP;EACA,QAAQ,MAAR;EAEA,SAAQ,KAAR;EACA,iBAAiB,OAAjB;EACA,aAAa,OAAb;EACA,gBAAgB,OAAhB;EACA,KAAK,KAAL;EACA,OAAO,KAAP;EACA,aAA0B,yBAA1B;EACA,WAAW,KAAX;;AAGD;EACC,UAAU,SAAV;EACA,OAAO,MAAP;EACA,QAAQ,MAAR;EACA,MAAM,KAAN;EACA,WAAsB,0BAAtB;;AAOD;EACC,SAAS,KAAT;EACA,UAAS,SAAT;EACA,KAAK,IAAL;EACA,MAAM,IAAN;EACA,YAAY,WAAZ;EACA,OAAO,MAAP;EACA,YAAW,MAAX;EACA,SAAS,KAAT;EACA,SAAS,IAAT;EACA,YAAY,KAAZ;EACA,OAAO,KAAP;EACA,aAA0B,yBAA1B;EACA,WAAW,KAAX;EACA,aAAa,IAAb;;AAGA;EACC,UAAS,SAAT;EACA,KAAK,KAAL;EACA,OAAO,KAAP;EACA,SAAS,KAAT;EACA,WAAW,KAAX;EACA,aAAY,KAAZ;EACA,QAAQ,QAAR;;AAGD;EACC,UAAS,SAAT;EACA,OAAO,IAAP;EACA,QAAQ,MAAR;EACA,YAAY,KAAZ;EACA,QAAQ,gBAAR;EACA,OAAO,KAAP;EACA,KAAK,MAAL;;AAGD;AAAI;AAAI;EACP,QAAQ,QAAR;EACA,aAAY,OAAZ;;AAGD;EACC,aAAY,KAAZ;;AAGD;EACC,QAAQ,WAAR;;AAGD;EACC,QAAQ,WAAR;EACA,QAAQ,KAAR;;AAGD;EACC,SAAS,aAAT;EACA,QAAQ,cAAR;EACA,SAAS,SAAT;EACA,QAAQ,eAAR;EACA,QAAQ,QAAR;EACA,aAAa,KAAb;;AACA;EACC,YAAY,KAAZ;EACA,OAAO,KAAP;;AAIF;EACC,QAAQ,cAAR;EACA,SAAS,EAAT;EACA,YAAY,KAAZ;;AAEA;EACC,QAAQ,WAAR;EACA,SAAS,IAAT;EACA,aAAY,KAAZ;;AAEA;EACC,SAAS,EAAT;;AAGD;EACC,WAAW,KAAX;EACA,gBAAgB,IAAhB;EACA,aAAa,KAAb;;AAGD;EACC,QAAQ,UAAR;;AAOD;EACC,QAAQ,QAAR;;AAEA;EACC,iBAAiB,UAAjB","file":"style.css"} -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | import Game from './game/Game.js' 2 | let Q = {} 3 | 4 | function getMask(x, y, width, height) { 5 | let canvas = document.createElement('canvas') 6 | let context = canvas.getContext('2d') 7 | canvas.width = width 8 | canvas.height = height 9 | document.body.appendChild(canvas); 10 | context.drawImage(Q.spriteImage, x, y, width, height, 0, 0, width, height) 11 | let array = [] 12 | 13 | for (let x = 0; x < width; x += 1) { 14 | array[x] = [] 15 | for (let y = 0; y < height; y += 1) { 16 | let data = context.getImageData(x, y, 1, 1).data 17 | let value = ' ' 18 | if (data[0] === 255) { 19 | value = 'x' 20 | } 21 | array[x][y] = value 22 | } 23 | } 24 | 25 | document.body.removeChild(canvas) 26 | 27 | return array 28 | } 29 | 30 | function spriteImageLoaded() { 31 | // Get masks 32 | Q.masks = [] 33 | Q.masks[0] = getMask(464, 1878, 156, 72) // Slime monster 34 | Q.masks[1] = getMask(286, 1834, 168, 116) // Flying saucer 35 | Q.masks[2] = getMask(630, 1870, 104, 80) // Pink Snake 36 | Q.masks[3] = Q.masks[2] // Yellow Snake 37 | Q.masks[4] = getMask(88, 1758, 188, 192) // Dragon 38 | Q.GAME = new Game() 39 | } 40 | 41 | function doubleClick(event) { 42 | if (document.webkitIsFullScreen) { 43 | document.webkitExitFullscreen() 44 | }else { 45 | document.body.webkitRequestFullscreen() 46 | } 47 | } 48 | 49 | function init() { 50 | Q.width = 720 51 | Q.height = 576 52 | Q.spriteImage = new Image() 53 | Q.spriteImage.addEventListener('load', spriteImageLoaded); 54 | Q.spriteImage.src = './assets/sprite.png' 55 | window.addEventListener('dblclick', doubleClick); 56 | } 57 | 58 | window.addEventListener('load', init) 59 | 60 | export default Q -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | html,body{margin:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;background:#000}.click-message{position:absolute;background:#000;width:720px;height:570px;display:flex;justify-content:center;align-items:center;flex-direction:column;gap:20px;color:#fff;font-family:'Roboto Mono',monospace;font-size:30px}#wrapper{position:relative;width:720px;height:570px;flex:none;transform:scaleX(1.65) scaleY(1.25)}#settings{display:none;position:absolute;top:0;left:0;box-sizing:border-box;width:100vw;min-height:100vh;padding:20px;z-index:100;background:#00f;color:#fff;font-family:'Roboto Mono',monospace;font-size:14px;line-height:1.5;}#settings .close-button{position:absolute;top:10px;right:10px;padding:20px;font-size:40px;line-height:20px;cursor:pointer}#settings #vizualizer{position:absolute;width:50%;height:300px;background:#fff;border:10px solid #fff;right:30px;top:200px}#settings h1,#settings h2,#settings h3{margin:0 0 0 0;font-weight:normal}#settings h1{font-weight:bold}#settings section{margin:20px 0 0 0}#settings #data-title{margin:0 0 10px 0;height:20px}#settings .button{display:inline-block;margin:0 10px 10px 0;padding:5px 10px;border:1px solid #fff;cursor:pointer;user-select:none;}#settings .button:active{background:#fff;color:#00f}#settings ul.sound-classes{margin:20px 0 0 20px;padding:0;list-style:none;}#settings ul.sound-classes li{margin:0 0 40px 0;opacity:.6;user-select:none;}#settings ul.sound-classes li:hover{opacity:1}#settings ul.sound-classes li .label{font-size:18px;letter-spacing:1px;font-weight:bold}#settings ul.sound-classes li .counter{margin:0 0 5px 0}#settings ul.sound-classes li .clear{cursor:pointer;}#settings ul.sound-classes li .clear:hover{text-decoration:underline} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kikk", 3 | "version": "1.0.0", 4 | "main": "js/main.js", 5 | "author": "Lasse Korsgaard", 6 | "license": "MIT", 7 | "browserify": { 8 | "transform": [ 9 | [ 10 | "babelify", 11 | { 12 | "presets": [ 13 | "es2015" 14 | ] 15 | } 16 | ] 17 | ] 18 | }, 19 | "scripts": { 20 | "build": "npm run html-copy && npm run style-build && npm run assets-copy && npm run browserify", 21 | "browserify": "browserify js/main.js -o public/bundle.js", 22 | "assets-copy": "cpx \"assets/**/*\" \"public/assets/\" --clean", 23 | "assets-watch": "npm run assets-copy -- --watch", 24 | "html-copy": "cpx \"html/**/*.html\" \"public/\" --clean", 25 | "html-watch": "npm run html-copy -- --watch", 26 | "style-watch": "stylus -m -w -u nib style/main.styl -o public/css/style.css", 27 | "style-build": "stylus -c -u nib style/main.styl -o public/css/style.css", 28 | "eslint": "eslint src", 29 | "stylint": "stylint style", 30 | "budo": "budo js/main.js:bundle.js --port=3000 --live -v --dir=public -o", 31 | "watch": "npm run style-build && concurrently \"npm run assets-watch\" \"npm run html-watch\" \"npm run budo\" \"npm run style-watch\"" 32 | }, 33 | "pre-commit": [ 34 | "eslint", 35 | "stylint" 36 | ], 37 | "dependencies": { 38 | "babel-preset-es2015": "^6.14.0", 39 | "babelify": "^7.3.0", 40 | "browserify": "^14.4.0", 41 | "budo": "^10.0.3", 42 | "concurrently": "^3.1.0", 43 | "cpx": "^1.5.0", 44 | "gsap": "^1.20.2", 45 | "knear": "^0.0.5", 46 | "meyda": "4.0.5", 47 | "ncp": "^2.0.0", 48 | "nib": "^1.1.2", 49 | "stylus": "^0.54.5" 50 | } 51 | } -------------------------------------------------------------------------------- /js/game/Title.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main' 3 | 4 | class Title { 5 | constructor(type) { 6 | this.counter = 0 7 | this.image = Q.spriteImage 8 | this.type = type 9 | 10 | if (this.type === 'main') { 11 | // this.width = 549 12 | // this.height = 99 13 | // this.x = 85 14 | // this.y = 98 15 | // this.sourceX = 0 16 | // this.sourceY = 1976 17 | // this.sourceWidth = 549 18 | // this.sourceHeight = 99 19 | 20 | this.width = 554 21 | this.height = 70 22 | this.x = 85 23 | this.y = 98 24 | this.sourceX = 626 25 | this.sourceY = 2112 26 | this.sourceWidth = 554 27 | this.sourceHeight = 70 28 | } 29 | 30 | if (this.type === 'jump') { 31 | this.width = 594 32 | this.height = 84 33 | this.x = 85 34 | this.y = 60 35 | this.sourceX = 0 36 | this.sourceY = 2252 37 | this.sourceWidth = 594 38 | this.sourceHeight = 84 39 | } 40 | 41 | if (this.type === 'duck') { 42 | this.width = 600 43 | this.height = 113 44 | this.x = 85 45 | this.y = 60 46 | this.sourceX = 0 47 | this.sourceY = 2091 48 | this.sourceWidth = 600 49 | this.sourceHeight = 113 50 | } 51 | 52 | if (this.type === 'retry') { 53 | this.width = 446 54 | this.height = 112 55 | this.x = 85 56 | this.y = 60 57 | this.sourceX = 0 58 | this.sourceY = 2372 59 | this.sourceWidth = 446 60 | this.sourceHeight = 112 61 | } 62 | 63 | } 64 | 65 | render(context) { 66 | if (Q.debug) { 67 | context.fillStyle = 'rgba(255,0,0,0.1)' 68 | context.fillRect(this.x, this.y, this.width, this.height) 69 | } 70 | 71 | 72 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 73 | } 74 | } 75 | 76 | export default Title -------------------------------------------------------------------------------- /js/game/Section.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main.js' 3 | import Obstacle from './Obstacle' 4 | 5 | class Section { 6 | constructor(x, noMonsters) { 7 | this.image = Q.spriteImage 8 | this.x = x 9 | this.y = Q.height - 108 10 | this.width = 1400 11 | this.height = 196 12 | 13 | this.sourceX = 428 14 | this.sourceY = 0 15 | this.sourceWidth = 1400 16 | this.sourceHeight = 196 17 | 18 | this.obstacles = [] 19 | 20 | let numObstacles = Math.floor(Math.random() * 2) + 1 21 | // let numObstacles = 10 22 | let basePosition = this.x 23 | 24 | if (noMonsters) { 25 | basePosition = 500 26 | numObstacles = 0 27 | } 28 | 29 | for (let index = 0; index < numObstacles; index += 1) { 30 | let type = Math.floor(Math.random() * 5) 31 | // let position = basePosition + Math.floor(Math.random() * (300 * index)) + ((Math.random() * 100) + (300 * index)) 32 | // if (position + 150 < this.width) { 33 | // let obstacle = new Obstacle(type, position) 34 | // this.obstacles.push(obstacle) 35 | // } 36 | let position = basePosition + (Math.random() * 150) 37 | // let position = basePosition + 100 38 | if (position + 200 - this.x < this.width - 100) { 39 | let obstacle = new Obstacle(type, position) 40 | this.obstacles.push(obstacle) 41 | basePosition += position + 200 + obstacle.width 42 | } 43 | } 44 | } 45 | 46 | render(context) { 47 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 48 | 49 | this.obstacles.forEach((obstacle) => { 50 | obstacle.x -= Q.speed 51 | obstacle.render(context) 52 | }) 53 | 54 | if (this.obstacles[0] && this.obstacles[0].x + this.obstacles[0].width < 0) { 55 | this.obstacles.shift() 56 | } 57 | } 58 | } 59 | 60 | export default Section -------------------------------------------------------------------------------- /js/game/ScoreBoard.js: -------------------------------------------------------------------------------- 1 | import Q from './../main.js' 2 | import Number from './Number' 3 | 4 | 5 | class ScoreBoard { 6 | constructor() { 7 | this.image = Q.spriteImage 8 | 9 | this.width = 100 10 | this.height = 52 11 | 12 | this.x = 14 13 | this.y = Q.height - this.height - 40 14 | 15 | this.sourceX = 116 16 | this.sourceY = 1444 17 | this.sourceWidth = 100 18 | this.sourceHeight = 52 19 | 20 | this.value = null 21 | this.numbers = [] 22 | this.numbers.push(new Number(0)) 23 | this.numbers.push(new Number(0)) 24 | this.numbers.push(new Number(0)) 25 | this.numbers.push(new Number(0)) 26 | 27 | // this.numbers[0].set(1) 28 | this.numbers[0].x = this.x + 12 29 | this.numbers[0].y = this.y + 12 30 | 31 | // this.numbers[1].set(2) 32 | this.numbers[1].x = this.x + 12 + (this.numbers[1].width * 1) 33 | this.numbers[1].y = this.y + 12 34 | 35 | // this.numbers[2].set(3) 36 | this.numbers[2].x = this.x + 12 + (this.numbers[2].width * 2) 37 | this.numbers[2].y = this.y + 12 38 | 39 | // this.numbers[3].set(4) 40 | this.numbers[3].x = this.x + 12 + (this.numbers[3].width * 3) 41 | this.numbers[3].y = this.y + 12 42 | } 43 | 44 | set(value) { 45 | this.value = value 46 | let array = value.toString().split('') 47 | while (array.length < 4) { 48 | array.unshift('x') 49 | } 50 | 51 | 52 | for (let index = 0; index < 4; index += 1) { 53 | if (array[3 - index]) { 54 | this.numbers[3 - index].set(array[3 - index]) 55 | } 56 | } 57 | } 58 | 59 | clear() { 60 | this.value = null 61 | } 62 | 63 | render(context) { 64 | context.drawImage(this.image, this.sourceX, this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 65 | 66 | this.numbers.forEach((number) => { 67 | number.render(context) 68 | }) 69 | } 70 | } 71 | 72 | export default ScoreBoard -------------------------------------------------------------------------------- /js/ai/KNN.js: -------------------------------------------------------------------------------- 1 | class KNN { 2 | constructor(topK) { 3 | this.topK = topK; 4 | this.data = []; 5 | } 6 | 7 | distance(arrayA, arrayB) { 8 | let sum = 0; 9 | arrayA.forEach((value, index) => { 10 | sum += Math.pow(value - arrayB[index], 2); 11 | }); 12 | 13 | return Math.sqrt(sum); 14 | } 15 | 16 | updateMax(value, array) { 17 | let max = 0; 18 | 19 | array.forEach((object) => { 20 | max = Math.max(max, object.distance); 21 | }); 22 | 23 | return max; 24 | } 25 | 26 | mode(array) { 27 | let frequency = {}; 28 | let max = 0; 29 | let result = null; 30 | 31 | for (let index = 0; index < array.length; index += 1) { 32 | let id = array[index]; 33 | frequency[id] = (frequency[id] || 0) + 1; 34 | if (frequency[id] > max) { 35 | max = frequency[id]; 36 | result = id; 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | 43 | predict(value) { 44 | let maxDistance = 0; 45 | let closest = []; 46 | let votes = []; 47 | 48 | 49 | this.data.forEach((object) => { 50 | let entry = { 51 | distance: this.distance(value, object.value), 52 | id: object.id 53 | }; 54 | 55 | if (closest.length < this.topK) { 56 | closest.push(entry); 57 | maxDistance = this.updateMax(maxDistance, closest); 58 | }else if (entry.distance < maxDistance) { 59 | let bool = true; 60 | let count = 0; 61 | while (bool) { 62 | if (Number(closest[count].distance) === maxDistance) { 63 | closest.splice(count, 1, entry); 64 | maxDistance = this.updateMax(maxDistance, closest); 65 | bool = false; 66 | }else if (count < closest.length - 1) { 67 | count += 1; 68 | }else { 69 | bool = false; 70 | } 71 | } 72 | } 73 | }); 74 | 75 | closest.forEach((entry) => { 76 | votes.push(entry.id); 77 | }); 78 | 79 | let prediction = this.mode(votes); 80 | 81 | return { 82 | prediction: prediction, 83 | votes: votes 84 | }; 85 | } 86 | 87 | learn(value, id) { 88 | this.data.push({ 89 | value: value, 90 | id: id 91 | }); 92 | } 93 | 94 | deleteClassData(id) { 95 | for (let index = this.data.length - 1; index >= 0; index -= 1) { 96 | let entry = this.data[index]; 97 | if (entry.id === id) { 98 | this.data.splice(index, 1); 99 | } 100 | } 101 | } 102 | } 103 | 104 | export default KNN -------------------------------------------------------------------------------- /html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sound-Controlled Intergalatic Teddy 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | Click anywhere to start game 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | × 25 |

Settings

26 | 27 | 28 | 29 |
30 |
31 | Save data set 32 | Load data set 33 | Clear all 34 | Change K: 10 35 | Change Thresold: 20 36 | Disable 37 | Enable 38 |
39 | 40 |
41 |

Prediction:

42 |
43 | 44 |
45 |

Classes

46 | 66 |
67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sound-Controlled Intergalatic Teddy 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | Click anywhere to start game 17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 | × 25 |

Settings

26 | 27 | 28 | 29 |
30 |
31 | Save data set 32 | Load data set 33 | Clear all 34 | Change K: 10 35 | Change Thresold: 20 36 | Disable 37 | Enable 38 |
39 | 40 |
41 |

Prediction:

42 |
43 | 44 |
45 |

Classes

46 | 66 |
67 |
68 | 69 | 70 | -------------------------------------------------------------------------------- /js/ai/Microphone.js: -------------------------------------------------------------------------------- 1 | import Meyda from 'meyda' 2 | 3 | class Microphone { 4 | constructor(bufferSize, callback) { 5 | if (Reflect.has(window, 'webkitAudioContext') && !Reflect.has(window, 'AudioContext')) { 6 | window.AudioContext = window.webkitAudioContext; 7 | } 8 | 9 | if (Reflect.has(navigator, 'webkitGetUserMedia') && !Reflect.has(navigator, 'getUserMedia')) { 10 | navigator.getUserMedia = navigator.webkitGetUserMedia; 11 | if (!Reflect.has(AudioContext, 'createScriptProcessor')) { 12 | AudioContext.prototype.createScriptProcessor = AudioContext.prototype.createJavaScriptNode; 13 | } 14 | } 15 | 16 | this.context = new AudioContext(); 17 | 18 | this.synthesizer = {}; 19 | this.synthesizer.out = this.context.createGain(); 20 | 21 | this.meyda = Meyda.createMeydaAnalyzer({ 22 | audioContext: this.context, 23 | source: this.synthesizer.out, 24 | bufferSize: bufferSize, 25 | featureExtractors: [ 26 | 'mfcc', 27 | 'loudness' 28 | ], 29 | callback: callback 30 | }); 31 | this.initializeMicrophoneSampling(); 32 | } 33 | 34 | initializeMicrophoneSampling() { 35 | let that = this; 36 | 37 | function errorCallback(err) { 38 | throw err; 39 | } 40 | 41 | function successCallback(mediaStream) { 42 | window.mediaStream = mediaStream; 43 | that.source = that.context.createMediaStreamSource(window.mediaStream); 44 | that.meyda.setSource(that.source); 45 | that.meyda.start(); 46 | 47 | console.groupEnd(); 48 | } 49 | 50 | try { 51 | navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.getUserMedia; 52 | 53 | let constraints = { 54 | video: false, 55 | audio: true 56 | }; 57 | 58 | try { 59 | navigator.getUserMedia(constraints, successCallback, errorCallback); 60 | }catch (data) { 61 | let getUserMedia = navigator.mediaDevices.getUserMedia(constraints); 62 | getUserMedia.then(successCallback); 63 | getUserMedia.catch(errorCallback); 64 | } 65 | }catch (data) { 66 | errorCallback(); 67 | } 68 | } 69 | } 70 | 71 | 72 | export default Microphone -------------------------------------------------------------------------------- /js/game/Icon.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main' 3 | 4 | class Icon { 5 | constructor(type) { 6 | this.counter = 0 7 | this.image = Q.spriteImage 8 | this.type = type 9 | 10 | this.canChange = true 11 | 12 | if (type === 'clap') { 13 | this.width = 144 14 | this.height = 204 15 | this.x = 250 16 | this.y = 200 17 | this.sourceX = 1396 18 | this.sourceY = 1754 19 | this.sourceWidth = 144 20 | this.sourceHeight = 204 21 | 22 | this.frame = 0 23 | this.numFrames = 2 24 | this.speed = 10 25 | 26 | } 27 | 28 | if (type === 'say') { 29 | this.width = 155 30 | this.height = 148 31 | this.x = 250 32 | this.y = 250 33 | this.sourceX = 931 34 | this.sourceY = 1812 35 | this.sourceWidth = 155 36 | this.sourceHeight = 148 37 | 38 | this.frame = 0 39 | this.numFrames = 2 40 | this.speed = 10 41 | } 42 | } 43 | 44 | render(context) { 45 | if (Q.debug) { 46 | context.fillStyle = 'rgba(255,0,0,0.1)' 47 | context.fillRect(this.x, this.y, this.width, this.height) 48 | } 49 | 50 | 51 | context.drawImage(this.image, this.sourceX + (this.sourceWidth * this.frame), this.sourceY, this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 52 | 53 | if (this.canChange) { 54 | if (this.counter < this.speed) { 55 | this.counter += 1 56 | }else { 57 | this.counter = 0 58 | 59 | if (this.frame < this.numFrames) { 60 | this.frame += 1 61 | }else { 62 | this.frame = 0 63 | if (this.triggered && this.type === 'clap') { 64 | this.canChange = false 65 | this.timer = setTimeout(() => { 66 | this.canChange = true 67 | this.triggered = false 68 | }, 1800) 69 | } 70 | 71 | if (this.triggered && this.type === 'say') { 72 | this.canChange = false 73 | this.timer = setTimeout(() => { 74 | this.canChange = true 75 | this.triggered = false 76 | }, 2200) 77 | } 78 | } 79 | 80 | if (this.type === 'clap' && this.frame === 2) { 81 | Q.player.duck() 82 | if (Q.clapSound) { 83 | Q.clapSound.play() 84 | } 85 | this.triggered = true 86 | } 87 | 88 | if (this.type === 'say' && this.frame === 2) { 89 | Q.player.jump() 90 | if (Q.ohSound) { 91 | Q.ohSound.play() 92 | } 93 | this.triggered = true 94 | } 95 | 96 | } 97 | } 98 | } 99 | } 100 | 101 | export default Icon -------------------------------------------------------------------------------- /style/main.styl: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0 3 | width: 100% 4 | height: 100% 5 | display: flex 6 | justify-content: center 7 | align-items: center 8 | background: #000 9 | } 10 | 11 | 12 | .click-message { 13 | position absolute; 14 | background black; 15 | width: 720px; 16 | height: 570px; 17 | 18 | display flex; 19 | justify-content: center; 20 | align-items: center; 21 | flex-direction: column; 22 | gap: 20px; 23 | color: #fff; 24 | font-family: 'Roboto Mono', monospace; 25 | font-size: 30px; 26 | } 27 | 28 | #wrapper { 29 | position: relative 30 | width: 720px 31 | height: 570px 32 | flex: none 33 | transform: scaleX(1.65) scaleY(1.25) 34 | } 35 | 36 | 37 | 38 | 39 | 40 | #settings { 41 | display: none 42 | position absolute 43 | top: 0px 44 | left: 0px 45 | box-sizing: border-box 46 | width: 100vw 47 | min-height 100vh 48 | padding: 20px 49 | z-index: 100 50 | background: blue 51 | color: #ffffff 52 | font-family: 'Roboto Mono', monospace 53 | font-size: 14px 54 | line-height: 1.5 55 | // overflow-y: scroll 56 | 57 | .close-button { 58 | position absolute 59 | top: 10px 60 | right: 10px 61 | padding: 20px 62 | font-size: 40px 63 | line-height 20px 64 | cursor: pointer 65 | } 66 | 67 | #vizualizer { 68 | position absolute 69 | width: 50% 70 | height: 300px 71 | background: #fff 72 | border: 10px solid #fff 73 | right: 30px 74 | top: 200px 75 | } 76 | 77 | h1, h2, h3 { 78 | margin: 0 0 0 0 79 | font-weight normal 80 | } 81 | 82 | h1 { 83 | font-weight bold 84 | } 85 | 86 | section { 87 | margin: 20px 0 0 0 88 | } 89 | 90 | #data-title { 91 | margin: 0 0 10px 0 92 | height: 20px 93 | } 94 | 95 | .button { 96 | display: inline-block 97 | margin: 0 10px 10px 0 98 | padding: 5px 10px 99 | border: 1px solid #fff 100 | cursor: pointer 101 | user-select: none 102 | &:active { 103 | background: #fff 104 | color: blue 105 | } 106 | } 107 | 108 | ul.sound-classes { 109 | margin: 20px 0 0 20px 110 | padding: 0 111 | list-style: none 112 | 113 | li { 114 | margin: 0 0 40px 0 115 | opacity: 0.6 116 | user-select none 117 | 118 | &:hover { 119 | opacity: 1 120 | } 121 | 122 | .label { 123 | font-size: 18px 124 | letter-spacing: 1px 125 | font-weight: bold 126 | } 127 | 128 | .counter { 129 | margin: 0 0 5px 0 130 | } 131 | 132 | .train { 133 | 134 | } 135 | 136 | .clear { 137 | cursor: pointer 138 | 139 | &:hover { 140 | text-decoration: underline 141 | } 142 | } 143 | } 144 | } 145 | 146 | 147 | } -------------------------------------------------------------------------------- /js/game/Player.js: -------------------------------------------------------------------------------- 1 | import TweenMax from "gsap"; 2 | import Q from "./../main.js"; 3 | 4 | class Player { 5 | constructor() { 6 | this.canChange = true; 7 | this.image = Q.spriteImage; 8 | 9 | this.width = 96; 10 | this.height = 108; 11 | 12 | this.baseX = this.x = 30; 13 | this.baseY = Q.height - this.height - 108; 14 | this.y = this.baseY; 15 | this.offsetY = 0; 16 | this.sourceWidth = 96; 17 | this.sourceHeight = 108; 18 | this.baseFrame = this.frame = 1; 19 | this.numFrames = 1; 20 | this.counter = 0; 21 | this.state = null; 22 | 23 | this.run(); 24 | } 25 | 26 | reset() { 27 | if (this.delay) { 28 | this.delay.kill(); 29 | } 30 | TweenMax.killTweensOf(this); 31 | // TweenMax.killChildTweensOf(this) 32 | this.canChange = true; 33 | this.x = this.baseX; 34 | this.y = this.baseY; 35 | } 36 | 37 | dead() { 38 | if (this.state !== "dead") { 39 | this.canChange = false; 40 | this.speed = 15; 41 | this.baseFrame = this.frame = 9; 42 | this.numFrames = 4; 43 | this.state = "dead"; 44 | this.delay = TweenMax.to(this, 0.3, { 45 | x: this.baseX - 10, 46 | }); 47 | 48 | let that = this; 49 | this.delay = TweenMax.to(this, 0.15, { 50 | y: this.baseY - 10, 51 | onComplete: () => { 52 | TweenMax.to(that, 0.15, { 53 | y: that.baseY, 54 | }); 55 | that.y = that.baseY; 56 | }, 57 | }); 58 | } 59 | } 60 | 61 | run() { 62 | if (this.delay) { 63 | this.delay.kill(); 64 | } 65 | this.offsetX = 20; 66 | this.offsetY = 0; 67 | this.speed = 5; 68 | this.baseFrame = this.frame = 2; 69 | this.numFrames = 1; 70 | this.state = "running"; 71 | } 72 | 73 | jump() { 74 | if (this.canChange) { 75 | if (this.delay) { 76 | this.delay.kill(); 77 | } 78 | 79 | this.canChange = false; 80 | this.baseFrame = this.frame = 7; 81 | this.numFrames = 1; 82 | this.speed = 4; 83 | this.state = "jumping"; 84 | let that = this; 85 | this.delay = TweenMax.to(that, 0.5, { 86 | y: that.baseY - 240, 87 | repeat: 1, 88 | yoyo: true, 89 | onComplete: () => { 90 | that.run(); 91 | that.canChange = true; 92 | }, 93 | }); 94 | } 95 | } 96 | 97 | duck() { 98 | if (this.canChange) { 99 | if (this.delay) { 100 | this.delay.kill(); 101 | } 102 | if (this.state != "ducking") { 103 | this.baseFrame = this.frame = 5; 104 | } 105 | this.offsetY = 34; 106 | this.numFrames = 1; 107 | this.speed = 4; 108 | this.state = "ducking"; 109 | this.delay = TweenMax.delayedCall(1.5, () => { 110 | this.run(); 111 | }); 112 | } 113 | } 114 | 115 | render(context) { 116 | if (Q.debug) { 117 | context.fillStyle = "rgba(0,255,0,0.1)"; 118 | context.fillRect( 119 | this.x + this.offsetX, 120 | this.y + this.offsetY, 121 | this.width - this.offsetX, 122 | this.height - this.offsetY 123 | ); 124 | } 125 | if (this.counter < this.speed) { 126 | this.counter += 1; 127 | } else { 128 | this.counter = 0; 129 | if (this.frame < this.baseFrame + this.numFrames) { 130 | this.frame += 1; 131 | } else { 132 | this.frame = this.baseFrame; 133 | } 134 | } 135 | 136 | context.drawImage( 137 | this.image, 138 | 0, 139 | this.sourceHeight * (this.frame - 1), 140 | this.sourceWidth, 141 | this.sourceHeight, 142 | this.x, 143 | this.y, 144 | this.width, 145 | this.height 146 | ); 147 | } 148 | 149 | pause() { 150 | if (this.delay) { 151 | this.delay.pause(); 152 | } 153 | } 154 | 155 | resume() { 156 | if (this.delay) { 157 | this.delay.resume(); 158 | } 159 | } 160 | } 161 | 162 | export default Player; 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sound Controlled Intergalactic Teddy 2 | 3 | #### Avoiding space monsters with sounds 4 | 5 | [![](assets/readme/video.jpg)](https://vimeo.com/241871305) 6 | 7 | [_Sound Controlled Intergalactic Teddy_](https://vimeo.com/241871305) is an infinite runner game where you use your voice and sounds to control Teddy’s movements. If you need to jump over a green slimy alien you say “Ohhh” and to duck you simply clap your hands. 8 | 9 | Using machine learning we have taught the game to understand and differentiate the sounds. For the game to be able to recognise and distinguish the sounds, we trained it by recording a lot of diverse _"ohhh”s_ and _claps_ which made it robust and easy for anyone to play. 10 | 11 | The installation was displayed in Namur, Belgium at [KIKK Festival 2017](https://www.kikk.be/2017/) as a part of [Little.KIKK](https://www.kikk.be/2017/en/little-kikk). 12 | 13 | __[Støj](http://stoj.io)__ is a creative coding studio run by ([Andreas Refsgaard](http://andreasrefsgaard.dk) & [Lasse Korsgaard](http://lassekorsgaard.com)) 14 | 15 | 16 | __Game graphics__: [Amalie Kvistgaard](http://amaliekvistgaard.dk) 17 | 18 | 19 | ## Demo 20 | You can play the game here: [demo](https://stoj-kikk.surge.sh/) (Make sure to allow microphone) 21 | 22 | 23 | ## Installation 24 | To install, clone this repository and then run `yarn` in Terminal. This will install all the dependencies. Once these are installed, run `yarn watch` - this will start a local server on your machine and open up the page in your browser and you're ready to play! 25 | 26 | ![Training interface](assets/readme/game.png) 27 | 28 | Be sure to allow the browser to use the microphone, otherwise it will not work. Also be aware that due to browser safety issues, this will only work on `localhost` and `https://` domains. 29 | 30 | ## Usage 31 | As per default the machine is trained on _Ohhh_ and _Clap_ sounds. 32 | If you would like to train it to recognize new sounds you can open up the training interface. 33 | 34 | To open the training interface simply hit `Shift + TAB` on your keyboard. 35 | 36 | ![Training interface](assets/readme/interface.png) 37 | 38 | ### Settings 39 | In the training interface you can load, save and reset training data. You can also control the threshold for how sensitive the microphone should be. if you are attempting to record some new training examples, but find that nothing happens, it could be because the `threshold` is too high. Click `Change Threshold` and set it to a lower number and try again. If the sound is loud enough the sound bars on the right will turn blue. 40 | 41 | 42 | ### Training 43 | You can clear all training data by clicking `Clear All`. 44 | 45 | Now say you want to train the sound that should trigger a jump. Click and hold down the `Train` button under _Jump_ and say the sound a few times (around 10-20 samples should be sufficient). Make sure the sound you're making is short and loud enough for the sound bars to turn completely blue, otherwise the sound is not recorded. 46 | 47 | 48 | 49 | 50 | ## License 51 | 52 | The MIT License (MIT) 53 | 54 | > Copyright 2017 [Støj](http://stoj.io) 55 | > 56 | > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 57 | > 58 | > The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 59 | > 60 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 61 | 62 | Sound Controlled Intergalactic Teddy makes use of the following libraries: 63 | 64 | - [__KNear__](https://github.com/NathanEpstein/KNear) by [Nathan Epstein](https://github.com/NathanEpstein). 65 | - License: MIT. Copyright (c) 2014 Nathan Epstein 66 | - [__meyda__](https://github.com/meyda/meyda) by [hughrawlinson](https://github.com/hughrawlinson), [nevosegal](https://github.com/nevosegal), [jakubfiala](https://github.com/jakubfiala) & [2xAA](https://github.com/2xaa) 67 | - License: MIT. Copyright (c) 2014 Hugh A. Rawlinson, Nevo Segal, Jakub Fiala -------------------------------------------------------------------------------- /js/game/Obstacle.js: -------------------------------------------------------------------------------- 1 | import TweenMax from 'gsap' 2 | import Q from './../main' 3 | 4 | class Obstacle { 5 | constructor(index, x) { 6 | this.counter = 0 7 | this.image = Q.spriteImage 8 | this.type = index 9 | this.mask = Q.masks[index] 10 | 11 | if (index === 0) { 12 | // Slime monster 13 | this.width = 156 14 | this.height = 72 15 | this.x = x 16 | this.y = Q.height - 110 - this.height 17 | this.sourceX = 116 18 | this.sourceY = 394 19 | this.sourceWidth = 156 20 | this.sourceHeight = 72 21 | 22 | this.frame = 0 23 | this.numFrames = 3 24 | this.speed = 10 25 | } 26 | 27 | 28 | if (index === 1) { 29 | // Flying saucer 30 | this.width = 168 31 | this.height = 116 32 | this.x = x 33 | this.y = Q.height - 190 - this.height 34 | this.sourceX = 116 35 | this.sourceY = 724 36 | this.sourceWidth = 168 37 | this.sourceHeight = 116 38 | 39 | this.frame = 0 40 | this.numFrames = 2 41 | this.speed = 50 42 | 43 | } 44 | 45 | 46 | if (index === 2) { 47 | // Pink snake 48 | this.width = 104 49 | this.height = 80 50 | this.x = x 51 | this.y = Q.height - 100 - this.height 52 | this.sourceX = 116 53 | this.sourceY = 1082 54 | this.sourceWidth = 104 55 | this.sourceHeight = 80 56 | 57 | this.frame = 0 58 | this.numFrames = 1 59 | this.speed = 20 60 | } 61 | 62 | 63 | if (index === 3) { 64 | // Yellow snake 65 | this.width = 104 66 | this.height = 80 67 | this.x = x 68 | this.y = Q.height - 100 - this.height 69 | this.sourceX = 116 70 | this.sourceY = 1252 71 | this.sourceWidth = 104 72 | this.sourceHeight = 80 73 | 74 | this.frame = 1 75 | this.numFrames = 1 76 | this.speed = 20 77 | } 78 | 79 | 80 | 81 | if (index === 4) { 82 | // Dragon 83 | this.width = 188 84 | this.height = 192 85 | this.x = x 86 | this.y = Q.height - 190 - this.height 87 | this.sourceX = 116 88 | this.sourceY = 0 89 | this.sourceWidth = 188 90 | this.sourceHeight = 192 91 | 92 | this.frame = 0 93 | this.numFrames = 1 94 | this.speed = 20 95 | } 96 | 97 | // this.width *= 0.8 98 | // this.height *= 0.8 99 | 100 | // this.y += (this.height * 0.25) 101 | } 102 | 103 | // getMask(x, y, width, height) { 104 | // let canvas = document.createElement('canvas') 105 | // let context = canvas.getContext('2d') 106 | // canvas.width = width 107 | // canvas.height = height 108 | // document.body.appendChild(canvas); 109 | // context.drawImage(this.image, x, y, width, height, 0, 0, width, height) 110 | 111 | 112 | // let array = [] 113 | 114 | // for (let x = 0; x < width; x += 1) { 115 | // array[x] = [] 116 | // for (let y = 0; y < height; y += 1) { 117 | // let data = context.getImageData(x, y, 1, 1).data 118 | // let value = ' ' 119 | // if (data[0] === 255) { 120 | // value = 'x' 121 | // } 122 | // array[x][y] = value 123 | // } 124 | // } 125 | 126 | // return array 127 | // } 128 | 129 | intersectRect(r1, r2) { 130 | return !(r2.left > r1.right || 131 | r2.right < r1.left || 132 | r2.top > r1.bottom || 133 | r2.bottom < r1.top); 134 | } 135 | 136 | hittest() { 137 | let a = { 138 | x: Q.player.x + Q.player.offsetX, 139 | y: Q.player.y + Q.player.offsetY, 140 | height: Q.player.height, 141 | width: Q.player.width 142 | 143 | } 144 | 145 | let b = this 146 | 147 | let xOverlap = Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) 148 | let yOverlap = Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y)) 149 | 150 | let minX = Math.min(a.x, b.x) 151 | let maxX = Math.max(a.x + a.width, b.x + b.width) 152 | let minY = Math.min(a.y, b.y) 153 | let maxY = Math.max(a.y + a.height, b.y + b.height) 154 | 155 | let baseX = 0 156 | let baseY = 0 157 | 158 | if (minX === a.x) { 159 | baseX = 0 160 | }else { 161 | baseX = a.x - b.x 162 | } 163 | 164 | if (minY === a.y) { 165 | baseY = 0 166 | }else { 167 | baseY = a.y - b.y 168 | } 169 | 170 | let found = false 171 | 172 | baseX = Math.floor(baseX) 173 | baseY = Math.floor(baseY) 174 | 175 | for (let x = 0; x < xOverlap; x += 1) { 176 | for (let y = 0; y < yOverlap; y += 1) { 177 | if (!found) { 178 | let char = this.mask[x + baseX][y + baseY] 179 | if (char === 'x') { 180 | found = true 181 | } 182 | } 183 | } 184 | } 185 | 186 | return found 187 | 188 | } 189 | 190 | render(context) { 191 | if (Q.debug) { 192 | context.fillStyle = 'rgba(255,0,0,0.1)' 193 | context.fillRect(this.x, this.y, this.width, this.height) 194 | } 195 | 196 | 197 | context.drawImage(this.image, this.sourceX, this.sourceY + (this.sourceHeight * this.frame), this.sourceWidth, this.sourceHeight, this.x, this.y, this.width, this.height) 198 | 199 | let test = this.intersectRect({ 200 | left: this.x, 201 | top: this.y, 202 | bottom: this.y + this.height, 203 | right: this.x + this.width 204 | }, { 205 | left: Q.player.x + Q.player.offsetX, 206 | top: Q.player.y + Q.player.offsetY, 207 | bottom: Q.player.y + Q.player.offsetY + Q.player.height, 208 | right: Q.player.x + Q.player.width - Q.player.offsetX 209 | }) 210 | 211 | if (test) { 212 | // Intersecting boundingboxes, check pixels 213 | let pixelHit = this.hittest() 214 | if (pixelHit) { 215 | let event = new CustomEvent('hit', { 216 | detail: { 217 | obstacle: this.type 218 | } 219 | }); 220 | window.dispatchEvent(event); 221 | } 222 | 223 | } 224 | 225 | if (this.counter < this.speed) { 226 | this.counter += 1 227 | }else { 228 | this.counter = 0 229 | 230 | if (this.frame < this.numFrames) { 231 | this.frame += 1 232 | }else { 233 | this.frame = 0 234 | } 235 | } 236 | } 237 | } 238 | 239 | export default Obstacle -------------------------------------------------------------------------------- /public/datasets/knips-weee.json: -------------------------------------------------------------------------------- 1 | {"jump":"[[77.66490563750267,43.26931576549235,17.457467334039528,8.29398159588121,2.742095232875538,0.8920145239545512,-1.2594466973302039,-1.9193657474289016,-3.489789351276039,-2.955327987533718,-3.7774350106132766,-2.6476521406435873,-2.6982124714879885],[46.0350893220093,43.08765998863798,36.83171799152805,26.98530726843738,17.244514266741668,7.905362489093089,1.412717862412332,-3.018783059030082,-4.985241963298916,-5.762052920417998,-6.008024526242345,-6.028462276804292,-6.551425822358412],[22.983376048202445,21.467452380397567,17.809403672368727,12.013572305775504,5.579609564937377,-1.0445835484132613,-6.535798602807099,-10.647044316342223,-12.820428402962351,-13.23758958456438,-12.133676231320498,-9.883275979014401,-7.174472943278231],[20.25185095197685,19.256141145628753,16.837167229910786,12.94647923071989,8.503066072623833,3.7526725190437245,-0.4228964401533377,-3.7935953394874287,-5.896509919598078,-6.766006489913892,-6.493623782683222,-5.294847001014356,-3.630649547625638],[25.77777573479807,24.866137910179162,22.698751083606144,19.07521392137541,14.808586343598646,9.963689174739802,5.322205043834353,1.0182207443880251,-2.4539029604773557,-5.021805084214904,-6.592245070502426,-7.166770950127239,-7.016910055773073],[29.48611965845339,28.232989313427066,25.139997984252265,20.46354635427956,15.13180011139155,9.567457839617475,4.551564493448352,0.31536348712403856,-2.7948889855898758,-4.803493202258357,-5.783153156570793,-5.894295281707811,-5.466681999687042],[27.443722022734164,26.185935248527475,23.190178663313368,18.38419746972289,12.996467074491706,7.273756281297709,2.2496454878125243,-1.9724851597948083,-4.935740081160204,-6.757851604092806,-7.5241922951547355,-7.393988106313734,-6.792575267138919],[26.31649056909737,25.250415584719704,22.887377760036852,18.94218380408716,14.498029386864047,9.500001979881946,4.930735954160663,0.8597153912603345,-2.1586002302147755,-4.216727375415365,-5.3261701609564644,-5.532092777967984,-5.19784645262218],[32.54143397588223,31.320913394698525,28.24401537174983,23.318419350541642,17.543119996108604,11.261656956612372,5.4337744385085704,0.35810664640263024,-3.4503347011616397,-5.9668524904828555,-7.229390812173598,-7.400102924150334,-6.887251241164693],[26.315376222896703,25.25073814019587,22.6078851361557,18.521997864227785,13.959341149927386,9.22703438868521,5.028232613099395,1.414213866383733,-1.3765351937076828,-3.448069352377013,-4.852031476301488,-5.653611066838803,-6.06366501114843],[27.575544359120613,26.526635145675602,23.97261263888534,19.85012828528932,15.1095383024957,9.9727102254694,5.29776142622757,1.2401355686273936,-1.7763138732267931,-3.795628346011222,-4.865569111488615,-5.115762268172067,-4.89570536058467],[25.40189656624443,24.386645703463394,21.94925766707922,18.07012796961546,13.714519297143882,9.071585731920148,4.908312902313121,1.275747026777913,-1.4882255930193362,-3.4622148363321537,-4.684070332296592,-5.2400201262043025,-5.415254132359738],[24.149889321725823,23.0611282120076,20.48874441713667,16.428931174194858,11.944060646975553,7.174386789848308,2.875460839957156,-0.9706680634455327,-3.985100777185535,-6.17436140758967,-7.461919998479458,-7.847125786236934,-7.618373027616737],[39.05011650024608,33.61014622150538,20.80903803628885,5.630104160058375,-5.315283203100886,-10.169728436110677,-9.603612450335032,-7.944804668353581,-8.2745677941766,-11.649168174288778,-15.819250184405016,-17.369845242363215,-14.582130634670376],[21.21931756891081,19.81373296006452,16.493754399537043,12.028664590233802,7.933280742146806,4.471679418344651,1.9014404699211165,-0.30103993609083557,-2.141091551082292,-3.6151067942549147,-4.480579831315051,-4.625389397212765,-4.343034226775403],[26.42246227830219,23.759244463650035,17.36255312472562,9.050951353772131,1.628962815136137,-3.7663512744320915,-6.1922767451750085,-6.5121528318792326,-5.684837272589176,-4.719492981185802,-3.8556856042804726,-3.054903963916847,-2.408941642347615],[28.640187573386356,23.150682319604112,14.61051438935292,6.440174747160295,2.9710811450155235,0.4306361487569102,-1.689039753746484,-5.746015997554854,-9.684752054410563,-12.679569126296716,-11.997580349446611,-7.732998145543243,-3.550162394295412],[30.632219595197967,25.432942297230902,13.124858008280627,-1.8857597033099425,-12.945340426839108,-17.73888235207045,-16.0543687874372,-11.572904570063324,-7.253676499356843,-4.937288693620779,-3.8718945586733877,-2.1218812579080097,1.2202671386776818],[29.07333566320449,26.496942019175197,21.72265741713746,13.65419577559954,5.970576968777379,-1.9740538166902752,-7.715688837444072,-12.293638157545681,-14.645246130077904,-15.422411822042935,-14.593074571253887,-12.163569352384556,-9.223500178754724],[21.348133801113363,20.119841200472923,17.63301439696279,13.373928364316615,8.866942323831733,3.9298479391328995,-0.1523338664381948,-3.52115410364084,-5.590104156091298,-6.664882637072669,-6.868719758743215,-6.356262304098535,-5.67795700064081]]","duck":"[[198.1642085313797,2.0420019745305504,-38.746938030130195,37.80411725314232,5.825257872924882,32.735366267846814,19.21420460542769,-14.843939741760758,-17.146724979385006,-5.560742054240601,11.745977236825384,-3.1601658083465476,-5.962668457937575],[177.23850719630718,5.446132674146088,-57.44944319629864,19.94059603807166,4.422479552733248,11.988157507777487,11.148462616197168,2.201781285929846,-11.952358602613874,0.6172503366860564,7.1125229180537906,-2.765072314086269,-3.6452685778779985],[113.40922890603542,1.7221055427773466,-37.05185192512032,25.780811887851822,20.15539803208129,18.181888914461712,10.367800952016955,3.016277455011855,-11.05910747806136,4.58949087215546,15.58627570751933,-5.853848651754805,-9.048744733288466],[52.43771991878748,5.20328903749718,-4.689834426758967,12.441998740082697,4.377857677065687,16.74707254996891,4.262373091542402,-9.159077535172212,-1.2754747710774175,-1.4922235086184112,4.412835366939598,-5.149292328689051,-8.9597042524109],[23.54982323409058,2.0473253185361737,-11.763290558177175,1.8078913239079255,6.3463608253275945,2.5033803182131775,-2.4573803604488043,-2.9457594450025146,-0.3007612491300673,1.445291080748399,1.9505680220783104,-1.9367984424703335,-3.071240471941103],[56.984243765473366,13.24476397439207,-2.5559036734746177,16.439116688438894,10.956204386064565,14.039101162837458,6.95664607577761,-12.568896088415055,-4.6768186842465,4.0904663438779005,-2.9597012398639873,-4.424672570043586,-1.891322079412265],[143.48390823602676,-12.123917762242382,-17.92232456582372,29.949522615893766,-7.445951436627154,41.237963018388285,24.294086426277858,-9.046392623597834,-13.582161173768156,6.588063720653379,14.29387694816696,9.743980339329505,-10.02623101496038],[48.73152182903141,26.818693091104855,8.988232300288226,15.038277129918287,24.106362068899283,27.940355163901,18.234963894825995,-0.8367740902142619,-2.2449821079968357,7.987049805337327,12.056819446864042,0.6902782220007229,-12.27574473785321],[61.87030660267919,6.867376006616835,-14.546991335610137,11.963791825598706,12.553525141558307,22.74889066028154,16.550952703096222,-7.7893341128100015,-3.6901523985580496,5.22289096336472,13.01445509215743,7.803568914093285,-11.288664823553118],[33.61239472287707,-2.978666212902821,-8.699095491806565,9.289197790030268,2.968725557943181,9.490350618103244,3.2587977435022792,-6.948750496178245,1.4255812365038687,1.823489984830596,1.8673537652124348,0.6629053707662189,-5.4659360004487],[134.31732960045338,-1.3212752278799134,-31.44334674027059,26.054417808928612,23.00669331715962,31.506377940023302,6.868307468444806,0.19584691822629016,-6.0981907786709,7.9142083861724775,7.560198831255139,2.075879138507684,-7.4353672009759935],[91.11921305209398,-10.295979013177451,-35.57184153097577,-3.2929791035394484,-11.59985630064292,18.28916715387521,9.892165585557265,-8.437107394679007,-9.47853717547168,8.764301070419851,4.9201623488965875,-2.379388244706633,-1.640361703621834],[64.24032291397452,-13.804770389002913,-27.923509566771397,9.451745804840185,-6.949987104995344,12.667038055886424,7.168372192929333,-12.028786216762189,-0.7249577524872723,3.913012482128686,4.0952024660514885,-2.938364286130533,-4.386864802064779],[67.26273985393345,-8.784270040237082,-31.257272712093553,4.183400302527695,1.935863354500373,15.288275126921896,16.177214301767346,-14.022458150782711,-13.101043676958247,5.229010007050661,10.47831532190157,5.234943054835469,-4.072090236519672],[121.83491930365562,27.666222837728707,-20.630773023924668,15.94771590254491,15.822080730177483,21.54086204587215,17.406113155729997,-5.20632084590571,-12.280734808956746,-10.500863683296837,0.01959312145201647,-3.6022249924776437,-14.057252717548629],[69.87899569235742,-17.31727096104618,-28.581930044169006,19.25740998589265,-11.35822976660847,-4.169909455825207,17.562488410674163,-2.492949736496214,-4.235697378660345,0.6080750723697145,4.359268327281224,-4.4086469989678365,-2.7977340856661557],[160.63450767099857,13.930900488064067,-34.001723642451616,25.533705712371596,-20.987647724780956,7.483303317801509,17.649796189916643,-5.756838642416709,-1.2300086016872513,-2.8438577620302916,12.009178830237202,-2.382075015005101,-3.9161530345792066],[93.33262585103512,-1.8334969910432999,-36.4168416586987,12.093085202428135,1.58263599704663,14.677927312169162,9.949811456194105,-10.115023135322055,0.6522611980074864,-7.090278805202079,-1.9740504437257116,8.472666338373294,-1.3336875790146157],[36.22474151477218,6.769034306134203,-13.458209958922659,-1.3739135839968262,9.95081958454298,11.604035534471686,5.129421227462105,-6.572932282546369,-2.9034840812729725,1.4665158382247765,0.013644540706566885,0.21455109176153492,-1.0508816927873565],[33.91327319992706,6.974311216710576,-5.864730451508853,4.387793631797542,6.892961200412929,12.271200829583428,16.17863313585371,-0.7720142106226062,-7.428062609841461,2.6420996832899486,9.351930477414589,5.605138718775847,-1.7223327256773815]]"} -------------------------------------------------------------------------------- /js/game/Game.js: -------------------------------------------------------------------------------- 1 | import Q from "./../main"; 2 | import Player from "./Player"; 3 | import Moon from "./Moon"; 4 | import Backdrop from "./Backdrop"; 5 | import Mountain from "./Mountain"; 6 | import Section from "./Section"; 7 | import Icon from "./Icon"; 8 | import Title from "./Title"; 9 | import BigNumber from "./BigNumber"; 10 | import ScoreBoard from "./ScoreBoard"; 11 | import SoundClassifier from "./../ai/AudioClassifier"; 12 | 13 | class Game { 14 | constructor() { 15 | this.paused = true; 16 | Q.speed = this.speed = 0; 17 | Q.debug = false; 18 | Q.clapSound = document.querySelector("#clap-sound"); 19 | Q.ohSound = document.querySelector("#oh-sound"); 20 | 21 | this.speedCounter = 0; 22 | this.resetCounter = 0; 23 | this.counter = 0; 24 | this.scoreBoard = new ScoreBoard(); 25 | Q.score = 0; 26 | Q.canvas = this.canvas = document.querySelector("#canvas"); 27 | this.canvas.width = Q.width; 28 | this.canvas.height = Q.height; 29 | this.context = this.canvas.getContext("2d"); 30 | 31 | Q.classes = {}; 32 | Q.allowTraining = false; 33 | 34 | this.clickStart = this.clickStart.bind(this); 35 | 36 | window.addEventListener("click", this.clickStart); 37 | // Q.soundClassifier = new SoundClassifier({ 38 | // k: 5, 39 | // threshold: 30 40 | // }) 41 | // window.addEventListener('prediction', this.predictionCallback.bind(this)); 42 | 43 | // Q.allowMicrophone = false 44 | // Q.player = this.player = new Player() 45 | 46 | // Q.duckIcon = new Icon('clap') 47 | // Q.jumpIcon = new Icon('say') 48 | // Q.title = new Title('main') 49 | // Q.jumpTitle = new Title('jump') 50 | // Q.duckTitle = new Title('duck') 51 | // Q.retryTitle = new Title('retry') 52 | // Q.countdownTitle = new BigNumber(3) 53 | // Q.countdownTitle.x = (720 - 80) / 2 54 | // Q.countdownTitle.y = (570 - 200) / 2 55 | 56 | // Q.showRetry = false 57 | // window.addEventListener('keydown', this.keydown.bind(this)) 58 | // window.addEventListener('hit', this.hit.bind(this)) 59 | 60 | // Q.timeoutTimer = null 61 | // this.reset(false) 62 | 63 | // this.playing = false 64 | // this.isFirst = 0 65 | } 66 | 67 | clickStart(event) { 68 | window.removeEventListener("click", this.clickStart); 69 | console.log("clickStart"); 70 | Q.soundClassifier = new SoundClassifier({ 71 | k: 5, 72 | threshold: 30, 73 | }); 74 | window.addEventListener("prediction", this.predictionCallback.bind(this)); 75 | 76 | Q.allowMicrophone = false; 77 | Q.player = this.player = new Player(); 78 | 79 | Q.duckIcon = new Icon("clap"); 80 | Q.jumpIcon = new Icon("say"); 81 | Q.title = new Title("main"); 82 | Q.jumpTitle = new Title("jump"); 83 | Q.duckTitle = new Title("duck"); 84 | Q.retryTitle = new Title("retry"); 85 | Q.countdownTitle = new BigNumber(3); 86 | Q.countdownTitle.x = (720 - 80) / 2; 87 | Q.countdownTitle.y = (570 - 200) / 2; 88 | 89 | Q.showRetry = false; 90 | window.addEventListener("keydown", this.keydown.bind(this)); 91 | window.addEventListener("hit", this.hit.bind(this)); 92 | 93 | Q.timeoutTimer = null; 94 | this.reset(false); 95 | 96 | this.playing = false; 97 | this.isFirst = 0; 98 | } 99 | 100 | reset(isIntro) { 101 | if (Q.tutorialTimeline) { 102 | Q.tutorialTimeline.kill(); 103 | } 104 | 105 | Q.showRetry = false; 106 | Q.showTitle = false; 107 | Q.showDuck = false; 108 | Q.showJump = false; 109 | Q.score = 0; 110 | this.resetCounter = 0; 111 | this.scoreBoard.set(Q.score); 112 | this.dead = false; 113 | 114 | this.backdrops = []; 115 | this.backdrops.push(new Backdrop(0)); 116 | 117 | this.mountainsBack = []; 118 | this.mountainsBack.push(new Mountain(0)); 119 | 120 | this.mountainsMiddle = []; 121 | this.mountainsMiddle.push(new Mountain(1)); 122 | 123 | this.mountainsFront = []; 124 | this.mountainsFront.push(new Mountain(2)); 125 | 126 | this.sections = []; 127 | this.sections.push(new Section(0, true)); 128 | 129 | Q.player.reset(); 130 | Q.score = 0; 131 | if (isIntro === true) { 132 | this.intro(); 133 | } else { 134 | Q.isIntro = false; 135 | 136 | if (Q.tutorialTimeline) { 137 | Q.tutorialTimeline.pause(); 138 | } 139 | Q.player.run(); 140 | this.startGame(); 141 | } 142 | } 143 | 144 | tutorialCompleted() { 145 | if (Q.tutorialTimeline) { 146 | Q.tutorialTimeline.kill(); 147 | } 148 | Q.showTitle = false; 149 | Q.showDuck = false; 150 | Q.showJump = false; 151 | Q.showRetry = false; 152 | 153 | Q.showScore = true; 154 | Q.isIntro = false; 155 | Q.soundClassifier.enable(); 156 | } 157 | 158 | startTutorial() { 159 | Q.soundClassifier.disable(); 160 | Q.showRetry = false; 161 | 162 | Q.tutorialTimeline = new TimelineMax({ 163 | onComplete: this.tutorialCompleted.bind(this), 164 | }); 165 | 166 | Q.showTitle = false; 167 | 168 | Q.tutorialTimeline.to(Q.duckIcon, 4.6, { 169 | delay: 0.5, 170 | onStart: () => { 171 | Q.showDuck = true; 172 | }, 173 | onComplete: () => { 174 | Q.showDuck = false; 175 | }, 176 | }); 177 | 178 | Q.tutorialTimeline.to(Q.jumpIcon, 5.2, { 179 | delay: 0.5, 180 | onStart: () => { 181 | Q.showJump = true; 182 | }, 183 | onComplete: () => { 184 | Q.showJump = false; 185 | }, 186 | }); 187 | 188 | let counter = 3; 189 | 190 | Q.tutorialTimeline.to(Q.countdownTitle, 1, { 191 | delay: 0.2, 192 | repeat: 3, 193 | onRepeat: () => { 194 | counter -= 1; 195 | if (counter < 1) { 196 | Q.countdownTitle.clear(); 197 | } else { 198 | Q.countdownTitle.set(counter); 199 | } 200 | }, 201 | onStart: () => { 202 | Q.countdownTitle.set(counter); 203 | Q.showCountdown = true; 204 | }, 205 | onComplete: () => { 206 | Q.showCountdown = false; 207 | }, 208 | }); 209 | } 210 | 211 | intro() { 212 | this.reset(); 213 | Q.speed = 4; 214 | Q.showScore = false; 215 | Q.player.run(); 216 | Q.score = 0; 217 | Q.isIntro = true; 218 | Q.showPlayer = true; 219 | Q.showRetry = false; 220 | 221 | Q.showTitle = true; 222 | 223 | Q.soundClassifier.enable(); 224 | if (this.timer) { 225 | cancelAnimationFrame(this.timer); 226 | } 227 | this.timer = requestAnimationFrame(this.render.bind(this)); 228 | } 229 | 230 | keydown(event) { 231 | if (event.keyCode === 13) { 232 | this.paused = !this.paused; 233 | if (this.paused) { 234 | this.stopGame(); 235 | } else { 236 | this.startGame(); 237 | } 238 | } else if (event.keyCode === 32) { 239 | this.reset(false); 240 | } else if (event.keyCode === 38) { 241 | event.preventDefault(); 242 | this.player.jump(); 243 | } else if (event.keyCode === 40) { 244 | event.preventDefault(); 245 | this.player.duck(); 246 | } 247 | } 248 | 249 | resetTimeout() { 250 | if (Q.timeoutTimer) { 251 | clearTimeout(Q.timeoutTimer); 252 | } 253 | // Q.timeoutTimer = setTimeout(this.intro.bind(this), 30000) 254 | } 255 | 256 | predictionCallback(event) { 257 | let prediction = event.detail.prediction.prediction; 258 | 259 | this.resetTimeout(); 260 | 261 | if (Q.isIntro) { 262 | this.startTutorial(); 263 | } else { 264 | if (prediction === "jump") { 265 | this.player.jump(); 266 | } 267 | 268 | if (prediction === "duck") { 269 | this.player.duck(); 270 | if (this.dead) { 271 | if (this.resetCounter < 1) { 272 | this.resetCounter += 1; 273 | } else { 274 | this.reset(false); 275 | } 276 | } 277 | } 278 | } 279 | } 280 | 281 | pause() { 282 | if (!this.paused) { 283 | this.paused = true; 284 | this.stopGame(); 285 | } 286 | } 287 | 288 | unpause() { 289 | if (this.paused) { 290 | this.paused = false; 291 | this.startGame(); 292 | } 293 | } 294 | 295 | startGame() { 296 | Q.isIntro = false; 297 | Q.showPlayer = true; 298 | Q.showScore = true; 299 | Q.showDuck = false; 300 | Q.showJump = false; 301 | Q.speed = 4; 302 | Q.player.run(); 303 | Q.score = 0; 304 | Q.soundClassifier.disable(); 305 | 306 | Q.soundTimer = setTimeout(() => { 307 | Q.soundClassifier.enable(); 308 | }, 600); 309 | 310 | this.stopGame(); 311 | this.timer = requestAnimationFrame(this.render.bind(this)); 312 | this.player.resume(); 313 | this.resetTimeout(); 314 | } 315 | 316 | stopGame() { 317 | if (this.timer) { 318 | cancelAnimationFrame(this.timer); 319 | } 320 | 321 | this.player.pause(); 322 | } 323 | 324 | hit(event) { 325 | Q.speed = 0; 326 | Q.player.dead(); 327 | this.dead = true; 328 | Q.showRetry = true; 329 | } 330 | 331 | render() { 332 | this.context.clearRect(0, 0, Q.width, Q.height); 333 | 334 | let removeBackdrop = false; 335 | this.backdrops.forEach((backdrop) => { 336 | backdrop.x -= Q.speed * 0; 337 | backdrop.render(this.context); 338 | 339 | if (backdrop.x + backdrop.width < Q.width && this.backdrops.length < 2) { 340 | this.backdrops.push(new Backdrop(backdrop.x + backdrop.width - 0.5)); 341 | } else if (backdrop.x + backdrop.width < 0) { 342 | removeBackdrop = true; 343 | } 344 | }); 345 | 346 | if (removeBackdrop) { 347 | this.backdrops.shift(); 348 | } 349 | 350 | this.mountainsBack.forEach((mountain) => { 351 | mountain.x -= Q.speed * 0.05; 352 | mountain.render(this.context); 353 | }); 354 | 355 | if ( 356 | this.mountainsBack.length === 1 && 357 | this.mountainsBack[0].x + this.mountainsBack[0].width < Q.width 358 | ) { 359 | let mountain = new Mountain(0); 360 | mountain.x = this.mountainsBack[0].x + this.mountainsBack[0].width - 0.5; 361 | this.mountainsBack.push(mountain); 362 | } 363 | 364 | if (this.mountainsBack[0].x + this.mountainsBack[0].width < 0) { 365 | this.mountainsBack.shift(); 366 | } 367 | 368 | this.mountainsMiddle.forEach((mountain) => { 369 | mountain.x -= Q.speed * 0.1; 370 | mountain.render(this.context); 371 | }); 372 | 373 | if ( 374 | this.mountainsMiddle.length === 1 && 375 | this.mountainsMiddle[0].x + this.mountainsMiddle[0].width < Q.width 376 | ) { 377 | let mountain = new Mountain(1); 378 | mountain.x = 379 | this.mountainsMiddle[0].x + this.mountainsMiddle[0].width - 0.5; 380 | this.mountainsMiddle.push(mountain); 381 | } 382 | 383 | if (this.mountainsMiddle[0].x + this.mountainsMiddle[0].width < 0) { 384 | this.mountainsMiddle.shift(); 385 | } 386 | 387 | this.mountainsFront.forEach((mountain) => { 388 | mountain.x -= Q.speed * 0.3; 389 | mountain.render(this.context); 390 | }); 391 | 392 | if ( 393 | this.mountainsFront.length === 1 && 394 | this.mountainsFront[0].x + this.mountainsFront[0].width < Q.width 395 | ) { 396 | let mountain = new Mountain(2); 397 | mountain.x = 398 | this.mountainsFront[0].x + this.mountainsFront[0].width - 0.5; 399 | this.mountainsFront.push(mountain); 400 | } 401 | 402 | if (this.mountainsFront[0].x + this.mountainsFront[0].width < 0) { 403 | this.mountainsFront.shift(); 404 | } 405 | 406 | let removeSection = false; 407 | this.sections.forEach((section) => { 408 | section.x -= Q.speed; 409 | section.render(this.context); 410 | 411 | if (section.x + section.width < Q.width && this.sections.length < 2) { 412 | this.sections.push( 413 | new Section(section.x + section.width - Q.speed, Q.isIntro) 414 | ); 415 | } else if (section.x + section.width < 0) { 416 | removeSection = true; 417 | } 418 | }); 419 | 420 | if (removeSection) { 421 | this.sections.shift(); 422 | } 423 | 424 | if (Q.showPlayer) { 425 | this.player.render(this.context); 426 | } 427 | 428 | if (!this.dead && this.counter % 10 === 0 && !Q.isIntro) { 429 | Q.score += 1; 430 | this.scoreBoard.set(Q.score); 431 | this.counter = 0; 432 | } 433 | 434 | this.counter += 1; 435 | 436 | if (Q.showScore) { 437 | this.scoreBoard.render(this.context); 438 | } 439 | 440 | if (!this.dead && !Q.isIntro) { 441 | if (this.speedCounter % 120 === 0) { 442 | Q.speed += 0.1; 443 | this.speedCounter = 0; 444 | } 445 | this.speedCounter += 1; 446 | } 447 | 448 | if (Q.showDuck) { 449 | Q.duckIcon.render(this.context); 450 | Q.duckTitle.render(this.context); 451 | } 452 | 453 | if (Q.showJump) { 454 | Q.jumpIcon.render(this.context); 455 | Q.jumpTitle.render(this.context); 456 | } 457 | 458 | if (Q.showTitle) { 459 | Q.title.render(this.context); 460 | } 461 | 462 | if (Q.showCountdown) { 463 | Q.countdownTitle.render(this.context); 464 | } 465 | 466 | if (Q.showRetry) { 467 | Q.retryTitle.render(this.context); 468 | } 469 | 470 | // Debug 471 | if (Q.debug) { 472 | this.context.fillStyle = "red"; 473 | this.context.fillRect(0, 0, 40, 40); 474 | this.context.fillStyle = "green"; 475 | this.context.fillRect(Q.width - 40, 0, 40, 40); 476 | this.context.fillStyle = "blue"; 477 | this.context.fillRect(Q.width - 40, Q.height - 40, 40, 40); 478 | } 479 | 480 | if (!this.renderOnce) { 481 | this.timer = requestAnimationFrame(this.render.bind(this)); 482 | } else { 483 | this.renderOnce = false; 484 | } 485 | } 486 | } 487 | 488 | export default Game; 489 | -------------------------------------------------------------------------------- /js/ai/AudioClassifier.js: -------------------------------------------------------------------------------- 1 | import Meyda from "meyda"; 2 | import KNN from "./KNN"; 3 | import Q from "./../main"; 4 | 5 | class AudioClassifier { 6 | constructor(config) { 7 | let defaultConfig = { 8 | k: 5, 9 | bufferSize: 512, 10 | trainPause: 400, 11 | threshold: 35, 12 | }; 13 | 14 | this.config = Object.assign(defaultConfig, config); 15 | this.allowBroadcasting = true; 16 | this.trainingData = []; 17 | this.training = false; 18 | this.allowTraining = false; 19 | this.predicting = false; 20 | this.currentClass = null; 21 | 22 | this.knn = new KNN(this.config.k); 23 | this.setupGUI(); 24 | 25 | if ( 26 | Reflect.has(window, "webkitAudioContext") && 27 | !Reflect.has(window, "AudioContext") 28 | ) { 29 | window.AudioContext = window.webkitAudioContext; 30 | } 31 | 32 | if ( 33 | Reflect.has(navigator, "webkitGetUserMedia") && 34 | !Reflect.has(navigator, "getUserMedia") 35 | ) { 36 | navigator.getUserMedia = navigator.webkitGetUserMedia; 37 | 38 | if (!Reflect.has(AudioContext, "createScriptProcessor")) { 39 | AudioContext.prototype.createScriptProcessor = 40 | AudioContext.prototype.createJavaScriptNode; 41 | } 42 | } 43 | 44 | this.context = new AudioContext(); 45 | 46 | this.synthesizer = {}; 47 | this.synthesizer.out = this.context.createGain(); 48 | 49 | let that = this; 50 | this.meyda = Meyda.createMeydaAnalyzer({ 51 | audioContext: this.context, 52 | source: this.synthesizer.out, 53 | bufferSize: this.config.bufferSize, 54 | featureExtractors: ["mfcc", "loudness"], 55 | callback: this.audioCallback.bind(that), 56 | }); 57 | this.initializeMicrophoneSampling(); 58 | 59 | this.renderer = null; 60 | this.peaked = false; 61 | window.addEventListener("resize", this.resize.bind(this)); 62 | } 63 | 64 | hideGUI() { 65 | this.hidden = true; 66 | this.element.style.display = "none"; 67 | cancelAnimationFrame(this.renderer); 68 | } 69 | 70 | showGUI() { 71 | this.hidden = false; 72 | this.element.style.display = "block"; 73 | this.renderer = requestAnimationFrame(this.render.bind(this)); 74 | this.resize(); 75 | } 76 | 77 | keyDown(event) { 78 | if (event.keyCode === 9 && event.shiftKey) { 79 | event.preventDefault(); 80 | 81 | if (this.hidden) { 82 | this.showGUI(); 83 | Q.GAME.pause(); 84 | } else { 85 | this.hideGUI(); 86 | // Q.GAME.unpause() 87 | Q.GAME.reset(false); 88 | } 89 | } 90 | } 91 | 92 | resize() { 93 | this.canvasWidth = this.canvas.width = this.canvas.offsetWidth; 94 | this.canvasHeight = this.canvas.height = this.canvas.offsetHeight; 95 | } 96 | 97 | render() { 98 | this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); 99 | this.ctx.fillStyle = this.peaked ? "blue" : "lightblue"; 100 | 101 | if (this.visual && this.visual.length > 0) { 102 | let bandWidth = this.canvasWidth / this.visual.length; 103 | this.visual.forEach((value, index) => { 104 | let height = value * (this.canvasHeight * 0.3); 105 | this.ctx.fillRect( 106 | index * bandWidth, 107 | this.canvasHeight - height, 108 | bandWidth + 1, 109 | height 110 | ); 111 | }); 112 | } 113 | this.renderer = requestAnimationFrame(this.render.bind(this)); 114 | } 115 | 116 | changeK() { 117 | let string = prompt("Set K (current value: " + this.knn.topK + ")"); 118 | if (string) { 119 | let value = parseInt(string); 120 | if (value > 0 && value < 40) { 121 | this.knn.topK = value; 122 | document.querySelector("#change-k").textContent = 123 | "Change K: " + this.knn.topK; 124 | } 125 | } 126 | } 127 | 128 | changeThreshold() { 129 | let string = prompt( 130 | "Set Threshold (current value: " + this.config.threshold + ")" 131 | ); 132 | if (string) { 133 | let value = parseInt(string); 134 | if (value > 0 && value < 200) { 135 | this.config.threshold = value; 136 | document.querySelector("#change-threshold").textContent = 137 | "Change Threshold: " + this.config.threshold; 138 | } 139 | } 140 | } 141 | 142 | trainingButtonDown(event) { 143 | let id = event.currentTarget.parentNode.id; 144 | window.addEventListener("mouseup", this.trainingButtonUpEvent); 145 | this.stopPredicting(); 146 | this.startTraining(id); 147 | } 148 | 149 | trainingButtonUp() { 150 | window.removeEventListener("mouseup", this.trainingButtonUpEvent); 151 | this.stopTraining(); 152 | this.startPredicting(); 153 | } 154 | 155 | confirmClear(event) { 156 | let id = event.currentTarget.parentNode.id; 157 | let test = confirm('Are you sure you want to clear "' + id + '"'); 158 | if (test) { 159 | this.clearClass(id); 160 | } 161 | } 162 | 163 | confirmClearAll(event) { 164 | let test = confirm("Are you sure you want to clear all classes"); 165 | if (test) { 166 | this.clearAll(); 167 | } 168 | } 169 | 170 | setDataSet(json) { 171 | this.clearAll(); 172 | this.trainingData = {}; 173 | let data = {}; 174 | 175 | for (let key in json) { 176 | data[key] = JSON.parse(json[key]); 177 | this.trainingData[key] = []; 178 | 179 | let length = data[key].length; 180 | for (let index = 0; index < length; index += 1) { 181 | this.knn.learn(data[key][index], key); 182 | this.trainingData[key].push(data[key][index]); 183 | } 184 | } 185 | this.updateSamples(); 186 | this.stopTraining(); 187 | this.startPredicting(); 188 | } 189 | 190 | saveDataSet() { 191 | let object = {}; 192 | for (let key in this.trainingData) { 193 | object[key] = JSON.stringify(this.trainingData[key]); 194 | } 195 | let string = JSON.stringify(object); 196 | let blob = new Blob([string], { type: "application/json" }); 197 | let url = URL.createObjectURL(blob); 198 | 199 | let link = document.createElement("a"); 200 | link.href = url; 201 | link.download = "dataset.json"; 202 | link.click(); 203 | } 204 | 205 | setFileName(name) { 206 | document.querySelector("#data-title").textContent = name; 207 | } 208 | 209 | loadDefaultDataSet() { 210 | let request = new XMLHttpRequest(); 211 | let filename = "datasets/default.json?t=" + new Date().getTime(); 212 | request.onreadystatechange = () => { 213 | if (request.readyState === 4 && request.status === 200) { 214 | let data = JSON.parse(request.responseText); 215 | this.setDataSet(data); 216 | this.setFileName("default.json"); 217 | } 218 | }; 219 | request.open("get", filename, true); 220 | request.send(); 221 | } 222 | 223 | loadDataSet() { 224 | let input = document.createElement("input"); 225 | input.type = "file"; 226 | input.addEventListener("change", (event) => { 227 | if (input.files.length > 0) { 228 | let file = input.files[0]; 229 | let name = file.name; 230 | let fileReader = new FileReader(); 231 | fileReader.onload = (event) => { 232 | let lines = event.target.result; 233 | let data = JSON.parse(lines); 234 | this.setDataSet(data); 235 | this.setFileName(name); 236 | }; 237 | fileReader.readAsText(file); 238 | } 239 | }); 240 | input.click(); 241 | } 242 | 243 | // toggle() { 244 | // this.allowPredicting = !this.allowPredicting 245 | // this.predicting = this.allowPredicting 246 | // if (this.allowPredicting) { 247 | // document.querySelector('#toggle').textContent = 'Disable prediction' 248 | // }else { 249 | // document.querySelector('#toggle').textContent = 'Enable prediction' 250 | // } 251 | // } 252 | 253 | enable() { 254 | this.allowBroadcasting = true; 255 | document.querySelector("#disable").style.opacity = 1; 256 | document.querySelector("#enable").style.opacity = 0.5; 257 | document.querySelector("#enable").textContent = "Enabled"; 258 | document.querySelector("#disable").textContent = "Disable"; 259 | } 260 | 261 | disable() { 262 | this.allowBroadcasting = false; 263 | document.querySelector("#disable").style.opacity = 0.5; 264 | document.querySelector("#disable").textContent = "Disabled"; 265 | document.querySelector("#enable").textContent = "Enable"; 266 | document.querySelector("#enable").style.opacity = 1; 267 | } 268 | 269 | setupGUI() { 270 | this.element = document.querySelector("#settings"); 271 | this.canvas = document.querySelector("#vizualizer"); 272 | this.ctx = this.canvas.getContext("2d"); 273 | this.hideGUI(); 274 | 275 | let closeButton = document.querySelector(".close-button"); 276 | closeButton.addEventListener("click", this.hideGUI.bind(this)); 277 | 278 | document 279 | .querySelector("#disable") 280 | .addEventListener("click", this.disable.bind(this)); 281 | document 282 | .querySelector("#enable") 283 | .addEventListener("click", this.enable.bind(this)); 284 | this.enable(); 285 | // document.querySelector('#toggle').addEventListener('click', this.toggle.bind(this)); 286 | document 287 | .querySelector("#save") 288 | .addEventListener("click", this.saveDataSet.bind(this)); 289 | document 290 | .querySelector("#load") 291 | .addEventListener("click", this.loadDataSet.bind(this)); 292 | document.querySelector("#change-k").textContent = 293 | "Change K: " + this.knn.topK; 294 | document 295 | .querySelector("#change-k") 296 | .addEventListener("click", this.changeK.bind(this)); 297 | 298 | document.querySelector("#change-threshold").textContent = 299 | "Change Threshold: " + this.config.threshold; 300 | document 301 | .querySelector("#change-threshold") 302 | .addEventListener("click", this.changeThreshold.bind(this)); 303 | document 304 | .querySelector("#clear") 305 | .addEventListener("click", this.confirmClearAll.bind(this)); 306 | this.trainingButtonUpEvent = this.trainingButtonUp.bind(this); 307 | this.trainingButtons = document.querySelectorAll(".sound-classes a.train"); 308 | this.trainingButtons.forEach((button) => { 309 | button.addEventListener("mousedown", this.trainingButtonDown.bind(this)); 310 | }); 311 | 312 | this.clearButtons = document.querySelectorAll(".sound-classes a.clear"); 313 | this.clearButtons.forEach((button) => { 314 | button.addEventListener("click", this.confirmClear.bind(this)); 315 | }); 316 | 317 | this.loadDefaultDataSet(); 318 | 319 | window.addEventListener("keydown", this.keyDown.bind(this)); 320 | } 321 | 322 | save() { 323 | let object = {}; 324 | for (let key in this.trainingData) { 325 | object[key] = JSON.stringify(this.trainingData[key]); 326 | } 327 | let string = JSON.stringify(object); 328 | } 329 | 330 | clearClass(name) { 331 | this.knn.deleteClassData(name); 332 | this.trainingData[name] = []; 333 | this.updateSamples(); 334 | } 335 | 336 | clearAll() { 337 | this.setFileName(""); 338 | for (let key in this.trainingData) { 339 | this.clearClass(key); 340 | } 341 | this.trainingData = {}; 342 | } 343 | 344 | initializeMicrophoneSampling() { 345 | let that = this; 346 | 347 | function errorCallback(err) { 348 | throw err; 349 | } 350 | 351 | function successCallback(mediaStream) { 352 | window.mediaStream = mediaStream; 353 | that.source = that.context.createMediaStreamSource(window.mediaStream); 354 | that.meyda.setSource(that.source); 355 | that.meyda.start(); 356 | } 357 | 358 | try { 359 | navigator.getUserMedia = 360 | navigator.webkitGetUserMedia || navigator.getUserMedia; 361 | 362 | let constraints = { 363 | video: false, 364 | audio: true, 365 | }; 366 | 367 | try { 368 | navigator.getUserMedia(constraints, successCallback, errorCallback); 369 | } catch (data) { 370 | let getUserMedia = navigator.mediaDevices.getUserMedia(constraints); 371 | getUserMedia.then(successCallback); 372 | getUserMedia.catch(errorCallback); 373 | } 374 | } catch (data) { 375 | console.log(data); 376 | errorCallback(); 377 | } 378 | } 379 | 380 | startTraining(name) { 381 | this.currentClass = name; 382 | if (!this.trainingData[this.currentClass]) { 383 | this.trainingData[this.currentClass] = []; 384 | } 385 | this.training = true; 386 | this.allowTraining = true; 387 | } 388 | 389 | stopTraining() { 390 | this.allowTraining = false; 391 | this.training = false; 392 | } 393 | 394 | startPredicting() { 395 | this.allowPredicting = true; 396 | this.predicting = true; 397 | } 398 | 399 | stopPredicting() { 400 | this.allowPredicting = false; 401 | this.predicting = false; 402 | } 403 | 404 | updateSamples() { 405 | let numSamples = {}; 406 | 407 | for (let key in this.trainingData) { 408 | numSamples[key] = this.trainingData[key].length; 409 | } 410 | 411 | this.trainingButtons.forEach((button) => { 412 | let id = button.parentNode.id; 413 | let counter = button.parentNode.children[1].children[0]; 414 | counter.textContent = numSamples[id]; 415 | }); 416 | 417 | let event = new CustomEvent("sample-update", { 418 | detail: { 419 | numSamples: numSamples, 420 | }, 421 | }); 422 | window.dispatchEvent(event); 423 | } 424 | 425 | audioCallback(data) { 426 | this.visual = data.loudness.specific; 427 | 428 | let peaked = false; 429 | for (let index = 0; index < data.mfcc.length; index += 1) { 430 | if (Math.abs(data.mfcc[index]) >= this.config.threshold) { 431 | peaked = true; 432 | } 433 | } 434 | this.peaked = peaked; 435 | 436 | if (this.training || this.predicting) { 437 | if (peaked) { 438 | if (this.training && this.allowTraining) { 439 | this.knn.learn(data.mfcc, this.currentClass); 440 | 441 | if (!this.trainingData[this.currentClass]) { 442 | this.trainingData[this.currentClass] = []; 443 | } 444 | this.trainingData[this.currentClass].push(data.mfcc); 445 | 446 | this.updateSamples(); 447 | 448 | if (this.timer) { 449 | clearTimeout(this.timer); 450 | } 451 | this.allowTraining = false; 452 | this.timer = setTimeout(() => { 453 | this.allowTraining = true; 454 | }, this.config.trainPause); 455 | } else if (this.predicting && this.allowPredicting) { 456 | let prediction = this.knn.predict(data.mfcc); 457 | document.querySelector("#prediction").textContent = 458 | prediction.prediction; 459 | if (this.allowBroadcasting) { 460 | let event = new CustomEvent("prediction", { 461 | detail: { prediction: prediction }, 462 | }); 463 | window.dispatchEvent(event); 464 | } 465 | 466 | if (this.timer) { 467 | clearTimeout(this.timer); 468 | } 469 | this.allowPredicting = false; 470 | this.timer = setTimeout(() => { 471 | document.querySelector("#prediction").textContent = ""; 472 | this.allowPredicting = true; 473 | }, this.config.trainPause); 474 | } 475 | } 476 | } 477 | } 478 | } 479 | 480 | export default AudioClassifier; 481 | -------------------------------------------------------------------------------- /public/datasets/default_40-40.json: -------------------------------------------------------------------------------- 1 | {"jump":"[[109.49668503645808,55.920622512298806,30.523723363456618,37.667856594526484,9.515314438178693,-9.708903403343983,-37.99551346120515,-25.833127645841348,-2.0169767201334237,-16.69646472354488,-13.698305880633535,5.240435968790032,7.191045363503159],[82.56025137841061,46.06910877750573,15.928368649549206,15.509206448012522,-6.066823301774169,-27.741057583202732,-41.05705926761334,-29.848258130099275,-4.209937603472162,-3.285780109112505,-5.978745793579349,5.424318998561891,9.94781833660738],[85.69063896762236,53.26209380563099,25.14022083550271,26.793462209289945,10.164182460957628,-15.981107363080715,-36.010976888635795,-28.893660335735763,-11.664511860969672,-14.210611712161864,-10.27463052207948,1.0827850718747831,6.7159720994517],[88.04179364673473,55.815066160387126,24.98249977353425,14.705803755874328,-5.875394585333252,-24.10677428374846,-42.094713239806396,-33.17379979484254,-9.41172167626462,-3.2499286289681897,-4.532015115573714,-3.339688279558521,6.242025188081976],[85.44123765934637,65.79872125249913,36.70928705832059,22.399594036805876,9.245798377599876,-7.6643152407468795,-21.940406264221362,-19.946733605400922,-0.7970807857405109,7.5414059780889655,-0.5538965203324273,-2.432301190411529,1.6902736158660043],[82.87239082749875,43.68890300828961,21.41624284879124,21.70701290454408,-11.199896555065042,-22.835789627067292,-30.917808829593625,-30.56400858693412,-8.736158850396892,-2.0579748345159365,-3.4332594955397013,-2.660309694645707,4.415838923297616],[80.2062403236705,53.239394904413814,19.10915643193085,14.595537423172324,5.522099126052732,-23.93505886647565,-42.62335486544609,-28.886757580982955,-9.628631610436509,-9.14009055537748,-9.309647812499499,1.0112085501070962,6.249209362083911],[84.535322048956,62.92396883473762,35.016233224858844,22.772480864669006,5.903241845143496,-10.11054829424767,-21.14357763129125,-22.491300307227267,-3.9434505636729917,8.364231792362467,-0.15140688552084602,-3.693947432252313,2.153719567582259],[81.44327664878074,61.99221684211966,37.418912832173525,27.019061150213137,11.885255303759326,-5.54150775636927,-20.185818084823453,-23.513682574411995,-10.310796531317374,1.2182539436829525,-0.8295614581030488,-3.8510716918705095,2.941964607837534],[80.88555094029653,57.8064467005705,30.852617638726894,23.070772513179772,6.48414856840893,-11.235284531458957,-20.720325990301514,-21.79019970559776,-5.150760718239535,7.68016440917752,-0.6264232719339694,-4.012024120174795,4.533948597425983],[88.6166254088821,40.02664783238239,-0.7278680036708645,10.128676430243885,-0.5764166948932107,-28.63843615878077,-48.2374680627223,-24.98239531121284,6.239232542494255,-2.1805066403305107,-3.8500560545176667,7.44782880130438,8.614895795760816],[88.95872421330569,63.53354510585772,30.20610296020447,15.585182810570018,-3.5800106681590855,-18.595616904854953,-22.208727435225047,-18.121773670260023,0.2817701033103576,12.512373516630355,1.9252249830887591,-7.013012298360269,-3.6011710205531453],[84.43523053686658,50.97347675706917,18.175444359052722,15.269087920872357,-1.8372181108224455,-26.56214771079423,-41.39724451435559,-28.355168383970803,-6.456580071206185,-5.504566557826023,-5.618347489536299,1.4928103879278265,4.08776691313188],[86.25547945643484,61.456389291510426,34.00821302301739,24.747047827909547,4.365667218048517,-13.060272020587774,-21.127938229595014,-21.121071327087726,-2.7663815374858136,8.081447712084374,-1.2696147985781716,-1.6380110480028014,4.3417475138803505],[82.50705946800372,49.94322191566044,12.814815771381955,8.666294067909929,-8.096256839739855,-35.19182953700329,-41.10463173818782,-26.485242445001422,-8.41976736721626,-3.7295475684256676,-8.759697297512165,-5.0129061946900535,1.3496535151114337],[91.21761959855576,77.35855496549169,51.44570426015472,28.809695026832365,11.330571000903255,-0.1917324181582646,-7.425541977559854,-8.56043845437137,-4.672228937488723,-1.3265317017901803,-0.12596582446609597,-0.16431287402866862,-0.017173138238081965],[89.48757732775448,66.16196355659709,37.52480569363568,24.706971666529835,5.5343853782037415,-10.628985075467135,-22.346116261334807,-25.95166498692313,-8.74402922614305,3.6056428623322443,-2.7052427086530617,-4.525200698904599,0.03525800589392544],[83.56886857342215,61.59412258318389,33.919664852930076,22.716702189479207,11.007725450233758,-7.744543471968261,-30.214342462714132,-26.974802867902184,-7.833704261572905,-3.926046299552183,-5.0566002689997225,-4.1908234930431965,2.344587823953147],[80.36043609783155,59.97549342120112,32.17197801786817,20.4642762481839,5.163636760878262,-13.826850613240252,-25.27742086236421,-23.26899369075354,-8.239598661559953,-0.227130437838244,-6.557125925037473,-7.8294417829540945,-0.7271899492559821],[81.435739141265,57.251264809872325,21.535945590157517,4.794739802649482,-8.8559555633458,-27.30623132405005,-38.781234934077474,-26.681682150730495,-5.84972817976942,-2.046506574710958,-6.0040885661536265,-3.258881341320397,3.1673887967130328],[81.39424982946366,61.03945746078174,31.812015213424523,19.776909916732585,5.806140183986494,-14.335489111214091,-24.370251441383804,-17.93108399964915,-1.9197617034105123,6.808914262415498,1.0664536230471056,-1.9780814357697964,4.2897532867391766],[81.36984565545936,67.50626903954735,40.94766751910772,19.42865958195079,2.132280983520647,-15.315188664277882,-26.915246587720652,-22.728553183556894,-9.092862597835394,-0.6998019804598545,-0.5329361800490028,-2.6032946550145613,-2.349737909915707],[81.1319153869972,50.588776192953674,19.313938015859918,18.091099647770864,2.486995801097697,-16.682286345319422,-25.04428757791889,-29.595848778276093,-15.689721490324871,-0.07878509369359155,-8.691004359568481,-12.856797454995068,2.061250499461434],[80.00324144919068,63.674452927927184,41.044532622643146,29.097894520042026,12.73393728584145,-5.708605737584833,-19.15444454210632,-22.32608707698181,-10.38631470431462,0.06524279252993355,-2.2677780633404305,-2.409451484525747,4.621284175673555],[81.01341822082577,70.11902258286067,49.61931225839941,31.974027820109793,16.35486833351133,-0.04224436036870121,-14.935458689063518,-18.112531216120438,-9.705744708136262,-1.7211946138488299,1.497363690138066,2.0592949813027874,2.5506554601945335],[81.4414648364691,60.871830441571404,32.66118854635557,21.16905600889147,5.803436625788172,-14.252648863523346,-25.675556138941666,-23.86584261513472,-9.73960391280717,-0.590071875638576,-7.0148511637533595,-9.315941362460409,0.21094566255529018],[82.27115757069987,45.873034778351595,8.929669061324597,5.780661877282162,-10.515057955375273,-37.3410025954956,-39.6038142450949,-13.238921950401012,-1.811765680686831,-0.4242609015629331,3.3923198490587323,1.136571210123602,0.3747798205863798],[101.62589658005163,58.047996840159875,-0.6651296248186442,-14.853111454882693,-20.64892574468791,-34.558399776717465,-36.76827783959129,-26.003718142131866,-15.257328081643179,-5.709169564676522,1.9663014340159763,10.393388398324142,19.71476716151978],[80.96938503472484,69.21115977719613,47.98405154187606,30.67287409932512,15.591452553547482,0.493126095529331,-11.98216008406746,-13.19151439816315,-5.683939292995347,0.5265073189918519,3.599422716756525,2.9791623323928214,0.7015855305600684],[82.80829321675265,61.5517778075303,34.020762678508966,24.10263230974249,7.768556507834174,-12.945243122498923,-22.36543463089132,-19.404939848392775,-6.06492049632167,4.7951390018652855,0.945839534378299,-3.2791682533548876,2.8028132247443818],[83.23835167193465,63.15511171434492,36.573814699029334,24.45573594472821,7.177738199400015,-11.680104141284597,-22.0596259769056,-19.891401505869943,-5.259824293356672,4.351943135097583,0.4034551175813202,-2.403126384265102,2.0496954723961087],[83.30525613997452,54.654243852965486,21.952036672366436,13.983086015161065,2.700109456849131,-16.54567117320337,-39.35350800982033,-34.652522776041515,-9.980874698533002,-4.171764837501586,-5.551596792914727,-5.421901874155172,-2.4866272900916258],[86.30923125984191,61.18596272544704,31.506690787552998,21.944783765439983,3.476369935455746,-14.068165731371854,-23.243628656604645,-24.413166535971747,-5.585805958899173,7.169454633573282,-2.7270340536660114,-5.6072156165093245,1.9502133364716874],[88.89263370633125,76.1430631240384,51.69208684628147,29.050899595841503,9.749610746880292,-6.174017524205905,-17.735575513798857,-17.267723602263604,-7.341444256474945,0.503017919067063,3.5369737308066234,3.406346927733844,3.0044446630961192],[91.42713737735176,74.56663288841862,48.02511242322079,29.2853728136642,10.362568287665756,-8.10287136043252,-19.843281404918915,-15.432495194325258,-3.24750364537226,1.1276138567619567,1.8331448219750617,0.9299711074584288,-1.5258871364855766],[81.35305859651817,61.07653243692784,34.443109790726965,24.135821694802246,7.778790513266668,-11.592229551585897,-20.45815411358526,-18.919367613350854,-5.792791093287705,4.460014351130335,-1.6454975557978482,-4.7074510830907,3.3134300791753883],[80.4641441646454,46.07478386326031,3.1018790200507467,-3.4966603952666877,-10.61035599460901,-27.841129724793067,-35.85410001392063,-27.156541437518285,-6.490051079709016,7.6432900243108,-0.651115406928802,-9.739268179609535,-0.5173685953747806],[121.41004752716981,74.07019958118956,5.2968758283880675,-20.63045760954949,-26.110091258625605,-28.52167606993706,-28.87723268912591,-20.73089814534322,-7.671398138278978,0.76390054927413,5.350398478895846,4.546745496602966,-1.7046880260114439],[85.94242748867691,62.18908396738849,32.80937736017247,20.595549025574158,0.6631086303809821,-16.295039727827007,-21.595915220731627,-16.318140488844058,1.415502421078077,7.639020846365699,-1.610622609080719,-2.7889928192829134,-0.03986692992470015],[85.33651334293609,48.92274238821307,17.466927097035242,15.682224935924399,-5.874936618032537,-26.455474527761435,-40.22167365018125,-34.44453896536387,-10.991461626543103,-2.3511856393036132,-2.6647551935863873,-0.09037148105184267,9.160060572142854]]","duck":"[[213.72990715503693,20.327600777228024,-16.627182257725433,10.037312180272785,-32.93975304918418,-15.123767029974111,-11.370390891764076,2.9490514234552085,-3.7189745278097295,6.685172340391493,12.486614308446972,3.380704812208394,8.454411053897285],[144.90205335244536,28.6786141718743,-27.940698197257937,16.809577033378705,-6.923293707878069,-10.51229465699062,-2.414599405087854,2.3007541497717177,-7.855888686808247,-11.858647309379545,-4.042300963452248,-6.78279514360814,1.0254833359917375],[166.37749598466326,25.44962549253476,-56.347268222698084,1.447096948208251,-20.673156146126637,-29.27494349832701,-10.376865327020335,3.8702694470923986,-5.404136027556131,-5.767506589868458,9.242721022902451,-0.3443517176262198,-4.192130730883832],[212.8680191040039,29.899820195177817,-70.06551555703979,-19.725099758306435,-24.421283280514004,-7.482703758761916,9.977574570902501,1.7532638866234622,-7.640441016407432,5.306985158643424,2.2268181570512713,-5.665316912675857,-3.950503881729863],[164.06615445017815,2.308549330561373,-63.299657644838724,-2.57994498751231,-1.3800016330641265,-4.805223373273417,0.7323485208707703,8.940351478014009,-13.539992911626285,7.43360008765412,2.179754434764019,-11.643226409230548,-4.012652319617728],[160.5943468878977,17.245557296056102,-77.20105863943212,-7.699110545533931,-16.607145809260142,-5.440264666432563,30.824673118255248,3.331651193437986,-7.773600814589116,-0.030201221550585685,-0.6218507442205966,-2.159388947456443,-7.963118254725149],[101.87539164870395,15.986900131617295,-51.163457719106795,-19.820020092221796,-9.066170746644417,0.19756527470287985,4.147942073844791,5.1103548986888425,9.071081092872427,-2.2725571000939864,-3.884473233356713,1.523924120308789,5.475518939224972],[244.59486734867096,21.165724175708327,-33.58210923700092,13.881452455229628,-26.740836978606865,-18.80706723668529,-18.62612820437212,-6.319027668471481,-2.4963269667899803,-1.5631513452654773,-5.930577100202634,-2.624043710672625,2.194293264968727],[93.9632253915479,58.33593150542007,7.866515534702599,-15.942384912027284,-27.806615374275957,-22.808426446882347,-1.5114872554406855,17.101492073534843,13.974870672731859,5.243137635570929,4.033019193580677,-0.8652116599347046,-3.817333669987109],[86.5259726610966,-20.55268357378687,-32.20558340769798,24.050048838972224,-14.03212700154023,-4.704459272057548,1.8632509371413781,4.9707559245613195,2.1474190684886034,2.4951620537935337,-1.682067680556285,-6.2419225316484015,-1.5229032175130965],[134.56968335434794,47.51912831449276,-32.25305144674159,-25.495617598483495,-18.14792489474181,0.14593606659778813,10.811690659799476,0.7916603903018679,-10.38863934570551,-7.269714682261688,0.42487455255749246,6.22627665669912,7.326474462644878],[233.79699164628983,24.740971882175337,-27.091076735629855,18.367655290305308,-20.293408637588126,-5.445363264065276,-9.280435264491915,9.565132600499494,-13.499098277664183,-5.421583858043869,-5.2063275998238066,-12.845042974082356,-7.68026255885599],[147.9714282527566,22.799775960242332,-58.96148627631604,-13.083007291911285,-22.40238920677378,-12.360097004836767,9.823616124517008,6.700984537764992,3.3646550956222403,2.666239924380014,3.2176474517730296,-0.1804407074094265,-4.309611916205883],[84.23857884702738,-4.579056408666227,-34.41448805666942,15.085033600532887,3.9104696099663605,-3.2153205533849265,-11.516950953409303,8.06433783180823,5.983690238591785,-5.432314587056703,3.757384137625082,-6.863669952064014,1.4493644331197453],[84.13982070796192,11.215880058360051,-37.57521817578528,0.027175688438818103,5.120992960357946,-3.4744170040494082,0.8542483336922032,1.5696104737789016,-0.944287730381493,3.9052994167805624,7.3944440565632,3.0887489589640493,2.2478416886399835],[108.78870451450348,29.788222910414547,-26.51004361656758,1.936490639058925,-6.181162294022369,-7.811297875561433,8.853478718704112,6.99154032332101,-4.155089048266724,-7.507346618420204,-4.335135248261488,-4.102394687887687,-0.11574923883362513],[187.7505221068859,46.91387502781528,-35.138238414669985,-2.746507552262115,-22.983136436747966,-7.638291156335102,1.0111667107360875,0.9224810615636587,-2.2825512565578014,-1.151726240306965,5.5390158565074685,6.133381688067678,6.788512074898497],[85.82041502412176,18.11620844610251,-36.52423933997108,-11.955058859895011,-7.9342834756716085,-8.410522570128228,2.1120498449351714,3.441619226086898,-1.9442785812028702,3.169607656145368,7.117326437077023,-5.735870426900051,-7.042855612998164],[174.11153348907828,22.60170816220315,-38.8767158176484,-7.522074650796314,-29.930861131190976,-9.792728649113608,-6.009043156704836,11.4289763748639,-10.779583529907251,-1.574864806960941,5.143124832929906,0.17390002769571122,8.120922904714469],[237.2187691628933,-8.269195240388235,-57.5390956796284,-14.525483084772551,-15.501257759143291,-1.2207320986389267,-14.522848012971984,-0.5302157825812706,-11.368314816074216,-3.9643249832256164,11.893532517253888,-2.3730086883360437,-2.5957638009958983],[198.91708221286535,39.38966095239249,-33.58295446866396,3.1514481710188478,-45.982844218009966,-24.103836277722902,-19.10454455641383,-7.918505800534733,-6.440172875000268,-16.567345977129726,6.500868410362017,1.3405613496433906,0.6483956274098149],[134.26208274811506,-1.1295369358097502,-55.732116845747846,-7.291308827250756,-7.363357503518128,4.907663473923722,-0.88337864916269,4.486323372210959,4.0291259996142985,-0.24903538394677222,-5.926390856268733,7.752317075584912,0.5761734852368001],[93.55586187238805,-1.8152304422678756,-31.259543444478606,14.70025275492636,-5.215775916668696,-4.578496721881876,-4.121864908976563,-1.4302340717358366,-6.155416980437976,0.36812366087954274,6.13845493570551,-5.420745888527868,-3.64769667989417],[199.47646338306367,18.567104811549832,-80.47188705313019,-10.166733688382426,-14.839174779197,-9.615901899481441,-10.90257013828711,-0.2888199282835119,5.336290258735052,-1.4573757246599284,2.0981838460244635,-1.5261943641840028,1.1788501492506884],[239.01704144477844,3.5335050259190686,-46.03663900943415,6.833234293162469,-14.462750701906334,-2.2838894272228676,-9.731555162441197,-5.58807732733651,-17.94055731418316,-1.597766528923329,2.9089679753794098,-2.436674901498196,-1.6055947659901797],[192.78171277046204,-4.46739562337269,-50.5690110800954,11.895040805603715,-8.048759733319304,-21.55710272189179,-14.6230082152796,-2.9249990474712324,-1.9443193342989928,0.258210547379919,-18.949827888149347,-0.6493375811263327,6.597120044225204],[206.06037655472755,27.93052426926596,-37.534412706336155,17.153552995472452,-15.035612608371078,-10.513983082654017,-4.8300721388443595,9.679743473865992,-1.8107118741625343,-2.5256990659431207,-5.44033264965849,-2.7648798740535874,4.372102993707287],[100.47642800211906,-14.803753303332432,-53.064570995782354,15.839157661459561,12.681721881007249,-14.67507498090633,-2.354097209057182,11.683922659353607,-4.954032603611052,4.534700150000468,-0.5847472489566827,-3.659830491074672,-0.4902948768238673],[94.12884770287201,15.09337529077091,-21.48182294193529,12.82874025232995,-6.25792316170609,-3.890757488251786,-2.3671147481489125,-7.243862070000201,-4.156829720656844,-6.340523667281478,3.7797341952868817,6.5779754804005375,-1.0362143187032673],[95.57812989875674,0.38855874165800475,-50.773422497342025,9.03321029770016,5.6311929571859265,-9.93146003315616,-2.3546806213311586,2.0371606456562574,2.4245174264420855,-2.5787356297922654,-3.3445478848250834,-1.6522756883741077,0.22045108401638147],[113.33015920594335,40.127122174128374,-35.70508089371835,-25.79537383472583,-9.1341284361957,2.3935359844532513,13.307322994841272,11.268105847942897,-0.9869861071347412,-4.844671540920964,0.24826734354149232,3.3269423800941964,3.283146436269289],[126.39723566966131,9.183878773770543,-55.160106493377995,-0.18707239459790365,-13.435519295195537,-7.776044479476531,4.944058377614783,4.355265368943194,3.113424953385149,-1.259569309382424,7.865833402948083,-4.798349841103526,-14.47299674807189],[189.65510255098343,23.950615187162406,-55.56216426561343,-2.9772789638101664,-11.97655179536469,-3.7081028437385033,7.384337972609293,5.040940761964561,3.1509604922446384,2.662823719376978,-2.8427893758566523,-7.056181684386741,-6.192136892606035],[175.46288003213704,21.503150617519474,-53.25227501011244,-6.493723212397822,-21.970610239491286,-11.907455062988728,-3.5085206371572233,8.004755369996975,-4.365710356604317,-10.456922984697666,0.6878756228317848,-5.4583616530371994,-4.701390321733451],[94.05710493854713,10.986088602334364,-36.8425773359097,-0.6202020400841324,-9.716297733382579,-4.070665964547782,6.153486320294905,1.6862524281190292,-1.8494106833481871,-7.061055817001808,-2.1295943532961994,-3.921055341825474,-7.373270719823432],[107.885397400707,7.085577280793136,-47.50943721056687,6.89439984697838,6.341144236745582,1.817151464196414,-0.49898739227524286,-9.448103253001523,-7.333810976880221,-5.274426547522844,-1.4834586493373627,-0.6995371622639457,-0.0870469320629382],[136.8851839825511,12.889653423158057,-37.51394587964225,20.62752642178137,-11.085846341016811,-19.912108469471658,-6.855214691861701,4.138788775234402,0.4201712179018974,-1.3907369030481762,0.4983875541024926,-15.484357158648184,-4.559610093210281],[113.22753800472128,30.2265435053945,-46.677896544776196,-20.30823552502237,1.4830313962673505,-2.248244274240264,-8.465344530318237,-12.395314937639485,-8.553348905218112,7.141353108015711,4.749694180055406,-6.644886192968403,-1.3305373282668456],[135.55295725504402,19.62067880424298,-46.73367336523344,-6.741187917303103,-20.24097182251455,-7.597630767714392,0.6729992201391498,-1.4252591360334188,-4.9498308144143905,-4.965639349265447,9.575336473052682,7.731639002819358,4.471520436861803],[150.25881531101186,21.802064338814798,-44.29274109687284,-4.235352830060731,-17.666143446644888,4.9798391833912055,-1.574840791115542,-6.854201532454216,-1.8143245686618155,-4.957256272327222,-0.6412573771044269,-6.95491703313055,-4.444404538369984]]"} -------------------------------------------------------------------------------- /public/datasets/default.json: -------------------------------------------------------------------------------- 1 | {"jump":"[[109.49668503645808,55.920622512298806,30.523723363456618,37.667856594526484,9.515314438178693,-9.708903403343983,-37.99551346120515,-25.833127645841348,-2.0169767201334237,-16.69646472354488,-13.698305880633535,5.240435968790032,7.191045363503159],[82.56025137841061,46.06910877750573,15.928368649549206,15.509206448012522,-6.066823301774169,-27.741057583202732,-41.05705926761334,-29.848258130099275,-4.209937603472162,-3.285780109112505,-5.978745793579349,5.424318998561891,9.94781833660738],[85.69063896762236,53.26209380563099,25.14022083550271,26.793462209289945,10.164182460957628,-15.981107363080715,-36.010976888635795,-28.893660335735763,-11.664511860969672,-14.210611712161864,-10.27463052207948,1.0827850718747831,6.7159720994517],[88.04179364673473,55.815066160387126,24.98249977353425,14.705803755874328,-5.875394585333252,-24.10677428374846,-42.094713239806396,-33.17379979484254,-9.41172167626462,-3.2499286289681897,-4.532015115573714,-3.339688279558521,6.242025188081976],[85.44123765934637,65.79872125249913,36.70928705832059,22.399594036805876,9.245798377599876,-7.6643152407468795,-21.940406264221362,-19.946733605400922,-0.7970807857405109,7.5414059780889655,-0.5538965203324273,-2.432301190411529,1.6902736158660043],[82.87239082749875,43.68890300828961,21.41624284879124,21.70701290454408,-11.199896555065042,-22.835789627067292,-30.917808829593625,-30.56400858693412,-8.736158850396892,-2.0579748345159365,-3.4332594955397013,-2.660309694645707,4.415838923297616],[80.2062403236705,53.239394904413814,19.10915643193085,14.595537423172324,5.522099126052732,-23.93505886647565,-42.62335486544609,-28.886757580982955,-9.628631610436509,-9.14009055537748,-9.309647812499499,1.0112085501070962,6.249209362083911],[84.535322048956,62.92396883473762,35.016233224858844,22.772480864669006,5.903241845143496,-10.11054829424767,-21.14357763129125,-22.491300307227267,-3.9434505636729917,8.364231792362467,-0.15140688552084602,-3.693947432252313,2.153719567582259],[81.44327664878074,61.99221684211966,37.418912832173525,27.019061150213137,11.885255303759326,-5.54150775636927,-20.185818084823453,-23.513682574411995,-10.310796531317374,1.2182539436829525,-0.8295614581030488,-3.8510716918705095,2.941964607837534],[80.88555094029653,57.8064467005705,30.852617638726894,23.070772513179772,6.48414856840893,-11.235284531458957,-20.720325990301514,-21.79019970559776,-5.150760718239535,7.68016440917752,-0.6264232719339694,-4.012024120174795,4.533948597425983],[88.6166254088821,40.02664783238239,-0.7278680036708645,10.128676430243885,-0.5764166948932107,-28.63843615878077,-48.2374680627223,-24.98239531121284,6.239232542494255,-2.1805066403305107,-3.8500560545176667,7.44782880130438,8.614895795760816],[88.95872421330569,63.53354510585772,30.20610296020447,15.585182810570018,-3.5800106681590855,-18.595616904854953,-22.208727435225047,-18.121773670260023,0.2817701033103576,12.512373516630355,1.9252249830887591,-7.013012298360269,-3.6011710205531453],[84.43523053686658,50.97347675706917,18.175444359052722,15.269087920872357,-1.8372181108224455,-26.56214771079423,-41.39724451435559,-28.355168383970803,-6.456580071206185,-5.504566557826023,-5.618347489536299,1.4928103879278265,4.08776691313188],[86.25547945643484,61.456389291510426,34.00821302301739,24.747047827909547,4.365667218048517,-13.060272020587774,-21.127938229595014,-21.121071327087726,-2.7663815374858136,8.081447712084374,-1.2696147985781716,-1.6380110480028014,4.3417475138803505],[82.50705946800372,49.94322191566044,12.814815771381955,8.666294067909929,-8.096256839739855,-35.19182953700329,-41.10463173818782,-26.485242445001422,-8.41976736721626,-3.7295475684256676,-8.759697297512165,-5.0129061946900535,1.3496535151114337],[91.21761959855576,77.35855496549169,51.44570426015472,28.809695026832365,11.330571000903255,-0.1917324181582646,-7.425541977559854,-8.56043845437137,-4.672228937488723,-1.3265317017901803,-0.12596582446609597,-0.16431287402866862,-0.017173138238081965],[89.48757732775448,66.16196355659709,37.52480569363568,24.706971666529835,5.5343853782037415,-10.628985075467135,-22.346116261334807,-25.95166498692313,-8.74402922614305,3.6056428623322443,-2.7052427086530617,-4.525200698904599,0.03525800589392544],[83.56886857342215,61.59412258318389,33.919664852930076,22.716702189479207,11.007725450233758,-7.744543471968261,-30.214342462714132,-26.974802867902184,-7.833704261572905,-3.926046299552183,-5.0566002689997225,-4.1908234930431965,2.344587823953147],[80.36043609783155,59.97549342120112,32.17197801786817,20.4642762481839,5.163636760878262,-13.826850613240252,-25.27742086236421,-23.26899369075354,-8.239598661559953,-0.227130437838244,-6.557125925037473,-7.8294417829540945,-0.7271899492559821],[81.435739141265,57.251264809872325,21.535945590157517,4.794739802649482,-8.8559555633458,-27.30623132405005,-38.781234934077474,-26.681682150730495,-5.84972817976942,-2.046506574710958,-6.0040885661536265,-3.258881341320397,3.1673887967130328],[81.39424982946366,61.03945746078174,31.812015213424523,19.776909916732585,5.806140183986494,-14.335489111214091,-24.370251441383804,-17.93108399964915,-1.9197617034105123,6.808914262415498,1.0664536230471056,-1.9780814357697964,4.2897532867391766],[81.36984565545936,67.50626903954735,40.94766751910772,19.42865958195079,2.132280983520647,-15.315188664277882,-26.915246587720652,-22.728553183556894,-9.092862597835394,-0.6998019804598545,-0.5329361800490028,-2.6032946550145613,-2.349737909915707],[81.1319153869972,50.588776192953674,19.313938015859918,18.091099647770864,2.486995801097697,-16.682286345319422,-25.04428757791889,-29.595848778276093,-15.689721490324871,-0.07878509369359155,-8.691004359568481,-12.856797454995068,2.061250499461434],[80.00324144919068,63.674452927927184,41.044532622643146,29.097894520042026,12.73393728584145,-5.708605737584833,-19.15444454210632,-22.32608707698181,-10.38631470431462,0.06524279252993355,-2.2677780633404305,-2.409451484525747,4.621284175673555],[81.01341822082577,70.11902258286067,49.61931225839941,31.974027820109793,16.35486833351133,-0.04224436036870121,-14.935458689063518,-18.112531216120438,-9.705744708136262,-1.7211946138488299,1.497363690138066,2.0592949813027874,2.5506554601945335],[81.4414648364691,60.871830441571404,32.66118854635557,21.16905600889147,5.803436625788172,-14.252648863523346,-25.675556138941666,-23.86584261513472,-9.73960391280717,-0.590071875638576,-7.0148511637533595,-9.315941362460409,0.21094566255529018],[82.27115757069987,45.873034778351595,8.929669061324597,5.780661877282162,-10.515057955375273,-37.3410025954956,-39.6038142450949,-13.238921950401012,-1.811765680686831,-0.4242609015629331,3.3923198490587323,1.136571210123602,0.3747798205863798],[101.62589658005163,58.047996840159875,-0.6651296248186442,-14.853111454882693,-20.64892574468791,-34.558399776717465,-36.76827783959129,-26.003718142131866,-15.257328081643179,-5.709169564676522,1.9663014340159763,10.393388398324142,19.71476716151978],[80.96938503472484,69.21115977719613,47.98405154187606,30.67287409932512,15.591452553547482,0.493126095529331,-11.98216008406746,-13.19151439816315,-5.683939292995347,0.5265073189918519,3.599422716756525,2.9791623323928214,0.7015855305600684],[82.80829321675265,61.5517778075303,34.020762678508966,24.10263230974249,7.768556507834174,-12.945243122498923,-22.36543463089132,-19.404939848392775,-6.06492049632167,4.7951390018652855,0.945839534378299,-3.2791682533548876,2.8028132247443818],[83.23835167193465,63.15511171434492,36.573814699029334,24.45573594472821,7.177738199400015,-11.680104141284597,-22.0596259769056,-19.891401505869943,-5.259824293356672,4.351943135097583,0.4034551175813202,-2.403126384265102,2.0496954723961087],[83.30525613997452,54.654243852965486,21.952036672366436,13.983086015161065,2.700109456849131,-16.54567117320337,-39.35350800982033,-34.652522776041515,-9.980874698533002,-4.171764837501586,-5.551596792914727,-5.421901874155172,-2.4866272900916258],[86.30923125984191,61.18596272544704,31.506690787552998,21.944783765439983,3.476369935455746,-14.068165731371854,-23.243628656604645,-24.413166535971747,-5.585805958899173,7.169454633573282,-2.7270340536660114,-5.6072156165093245,1.9502133364716874],[88.89263370633125,76.1430631240384,51.69208684628147,29.050899595841503,9.749610746880292,-6.174017524205905,-17.735575513798857,-17.267723602263604,-7.341444256474945,0.503017919067063,3.5369737308066234,3.406346927733844,3.0044446630961192],[91.42713737735176,74.56663288841862,48.02511242322079,29.2853728136642,10.362568287665756,-8.10287136043252,-19.843281404918915,-15.432495194325258,-3.24750364537226,1.1276138567619567,1.8331448219750617,0.9299711074584288,-1.5258871364855766],[81.35305859651817,61.07653243692784,34.443109790726965,24.135821694802246,7.778790513266668,-11.592229551585897,-20.45815411358526,-18.919367613350854,-5.792791093287705,4.460014351130335,-1.6454975557978482,-4.7074510830907,3.3134300791753883],[80.4641441646454,46.07478386326031,3.1018790200507467,-3.4966603952666877,-10.61035599460901,-27.841129724793067,-35.85410001392063,-27.156541437518285,-6.490051079709016,7.6432900243108,-0.651115406928802,-9.739268179609535,-0.5173685953747806],[121.41004752716981,74.07019958118956,5.2968758283880675,-20.63045760954949,-26.110091258625605,-28.52167606993706,-28.87723268912591,-20.73089814534322,-7.671398138278978,0.76390054927413,5.350398478895846,4.546745496602966,-1.7046880260114439],[85.94242748867691,62.18908396738849,32.80937736017247,20.595549025574158,0.6631086303809821,-16.295039727827007,-21.595915220731627,-16.318140488844058,1.415502421078077,7.639020846365699,-1.610622609080719,-2.7889928192829134,-0.03986692992470015],[85.33651334293609,48.92274238821307,17.466927097035242,15.682224935924399,-5.874936618032537,-26.455474527761435,-40.22167365018125,-34.44453896536387,-10.991461626543103,-2.3511856393036132,-2.6647551935863873,-0.09037148105184267,9.160060572142854]]","duck":"[[213.72990715503693,20.327600777228024,-16.627182257725433,10.037312180272785,-32.93975304918418,-15.123767029974111,-11.370390891764076,2.9490514234552085,-3.7189745278097295,6.685172340391493,12.486614308446972,3.380704812208394,8.454411053897285],[144.90205335244536,28.6786141718743,-27.940698197257937,16.809577033378705,-6.923293707878069,-10.51229465699062,-2.414599405087854,2.3007541497717177,-7.855888686808247,-11.858647309379545,-4.042300963452248,-6.78279514360814,1.0254833359917375],[166.37749598466326,25.44962549253476,-56.347268222698084,1.447096948208251,-20.673156146126637,-29.27494349832701,-10.376865327020335,3.8702694470923986,-5.404136027556131,-5.767506589868458,9.242721022902451,-0.3443517176262198,-4.192130730883832],[212.8680191040039,29.899820195177817,-70.06551555703979,-19.725099758306435,-24.421283280514004,-7.482703758761916,9.977574570902501,1.7532638866234622,-7.640441016407432,5.306985158643424,2.2268181570512713,-5.665316912675857,-3.950503881729863],[164.06615445017815,2.308549330561373,-63.299657644838724,-2.57994498751231,-1.3800016330641265,-4.805223373273417,0.7323485208707703,8.940351478014009,-13.539992911626285,7.43360008765412,2.179754434764019,-11.643226409230548,-4.012652319617728],[160.5943468878977,17.245557296056102,-77.20105863943212,-7.699110545533931,-16.607145809260142,-5.440264666432563,30.824673118255248,3.331651193437986,-7.773600814589116,-0.030201221550585685,-0.6218507442205966,-2.159388947456443,-7.963118254725149],[101.87539164870395,15.986900131617295,-51.163457719106795,-19.820020092221796,-9.066170746644417,0.19756527470287985,4.147942073844791,5.1103548986888425,9.071081092872427,-2.2725571000939864,-3.884473233356713,1.523924120308789,5.475518939224972],[244.59486734867096,21.165724175708327,-33.58210923700092,13.881452455229628,-26.740836978606865,-18.80706723668529,-18.62612820437212,-6.319027668471481,-2.4963269667899803,-1.5631513452654773,-5.930577100202634,-2.624043710672625,2.194293264968727],[93.9632253915479,58.33593150542007,7.866515534702599,-15.942384912027284,-27.806615374275957,-22.808426446882347,-1.5114872554406855,17.101492073534843,13.974870672731859,5.243137635570929,4.033019193580677,-0.8652116599347046,-3.817333669987109],[86.5259726610966,-20.55268357378687,-32.20558340769798,24.050048838972224,-14.03212700154023,-4.704459272057548,1.8632509371413781,4.9707559245613195,2.1474190684886034,2.4951620537935337,-1.682067680556285,-6.2419225316484015,-1.5229032175130965],[134.56968335434794,47.51912831449276,-32.25305144674159,-25.495617598483495,-18.14792489474181,0.14593606659778813,10.811690659799476,0.7916603903018679,-10.38863934570551,-7.269714682261688,0.42487455255749246,6.22627665669912,7.326474462644878],[233.79699164628983,24.740971882175337,-27.091076735629855,18.367655290305308,-20.293408637588126,-5.445363264065276,-9.280435264491915,9.565132600499494,-13.499098277664183,-5.421583858043869,-5.2063275998238066,-12.845042974082356,-7.68026255885599],[147.9714282527566,22.799775960242332,-58.96148627631604,-13.083007291911285,-22.40238920677378,-12.360097004836767,9.823616124517008,6.700984537764992,3.3646550956222403,2.666239924380014,3.2176474517730296,-0.1804407074094265,-4.309611916205883],[84.23857884702738,-4.579056408666227,-34.41448805666942,15.085033600532887,3.9104696099663605,-3.2153205533849265,-11.516950953409303,8.06433783180823,5.983690238591785,-5.432314587056703,3.757384137625082,-6.863669952064014,1.4493644331197453],[84.13982070796192,11.215880058360051,-37.57521817578528,0.027175688438818103,5.120992960357946,-3.4744170040494082,0.8542483336922032,1.5696104737789016,-0.944287730381493,3.9052994167805624,7.3944440565632,3.0887489589640493,2.2478416886399835],[108.78870451450348,29.788222910414547,-26.51004361656758,1.936490639058925,-6.181162294022369,-7.811297875561433,8.853478718704112,6.99154032332101,-4.155089048266724,-7.507346618420204,-4.335135248261488,-4.102394687887687,-0.11574923883362513],[187.7505221068859,46.91387502781528,-35.138238414669985,-2.746507552262115,-22.983136436747966,-7.638291156335102,1.0111667107360875,0.9224810615636587,-2.2825512565578014,-1.151726240306965,5.5390158565074685,6.133381688067678,6.788512074898497],[85.82041502412176,18.11620844610251,-36.52423933997108,-11.955058859895011,-7.9342834756716085,-8.410522570128228,2.1120498449351714,3.441619226086898,-1.9442785812028702,3.169607656145368,7.117326437077023,-5.735870426900051,-7.042855612998164],[174.11153348907828,22.60170816220315,-38.8767158176484,-7.522074650796314,-29.930861131190976,-9.792728649113608,-6.009043156704836,11.4289763748639,-10.779583529907251,-1.574864806960941,5.143124832929906,0.17390002769571122,8.120922904714469],[237.2187691628933,-8.269195240388235,-57.5390956796284,-14.525483084772551,-15.501257759143291,-1.2207320986389267,-14.522848012971984,-0.5302157825812706,-11.368314816074216,-3.9643249832256164,11.893532517253888,-2.3730086883360437,-2.5957638009958983],[198.91708221286535,39.38966095239249,-33.58295446866396,3.1514481710188478,-45.982844218009966,-24.103836277722902,-19.10454455641383,-7.918505800534733,-6.440172875000268,-16.567345977129726,6.500868410362017,1.3405613496433906,0.6483956274098149],[134.26208274811506,-1.1295369358097502,-55.732116845747846,-7.291308827250756,-7.363357503518128,4.907663473923722,-0.88337864916269,4.486323372210959,4.0291259996142985,-0.24903538394677222,-5.926390856268733,7.752317075584912,0.5761734852368001],[93.55586187238805,-1.8152304422678756,-31.259543444478606,14.70025275492636,-5.215775916668696,-4.578496721881876,-4.121864908976563,-1.4302340717358366,-6.155416980437976,0.36812366087954274,6.13845493570551,-5.420745888527868,-3.64769667989417],[199.47646338306367,18.567104811549832,-80.47188705313019,-10.166733688382426,-14.839174779197,-9.615901899481441,-10.90257013828711,-0.2888199282835119,5.336290258735052,-1.4573757246599284,2.0981838460244635,-1.5261943641840028,1.1788501492506884],[239.01704144477844,3.5335050259190686,-46.03663900943415,6.833234293162469,-14.462750701906334,-2.2838894272228676,-9.731555162441197,-5.58807732733651,-17.94055731418316,-1.597766528923329,2.9089679753794098,-2.436674901498196,-1.6055947659901797],[192.78171277046204,-4.46739562337269,-50.5690110800954,11.895040805603715,-8.048759733319304,-21.55710272189179,-14.6230082152796,-2.9249990474712324,-1.9443193342989928,0.258210547379919,-18.949827888149347,-0.6493375811263327,6.597120044225204],[206.06037655472755,27.93052426926596,-37.534412706336155,17.153552995472452,-15.035612608371078,-10.513983082654017,-4.8300721388443595,9.679743473865992,-1.8107118741625343,-2.5256990659431207,-5.44033264965849,-2.7648798740535874,4.372102993707287],[100.47642800211906,-14.803753303332432,-53.064570995782354,15.839157661459561,12.681721881007249,-14.67507498090633,-2.354097209057182,11.683922659353607,-4.954032603611052,4.534700150000468,-0.5847472489566827,-3.659830491074672,-0.4902948768238673],[94.12884770287201,15.09337529077091,-21.48182294193529,12.82874025232995,-6.25792316170609,-3.890757488251786,-2.3671147481489125,-7.243862070000201,-4.156829720656844,-6.340523667281478,3.7797341952868817,6.5779754804005375,-1.0362143187032673],[95.57812989875674,0.38855874165800475,-50.773422497342025,9.03321029770016,5.6311929571859265,-9.93146003315616,-2.3546806213311586,2.0371606456562574,2.4245174264420855,-2.5787356297922654,-3.3445478848250834,-1.6522756883741077,0.22045108401638147],[113.33015920594335,40.127122174128374,-35.70508089371835,-25.79537383472583,-9.1341284361957,2.3935359844532513,13.307322994841272,11.268105847942897,-0.9869861071347412,-4.844671540920964,0.24826734354149232,3.3269423800941964,3.283146436269289],[126.39723566966131,9.183878773770543,-55.160106493377995,-0.18707239459790365,-13.435519295195537,-7.776044479476531,4.944058377614783,4.355265368943194,3.113424953385149,-1.259569309382424,7.865833402948083,-4.798349841103526,-14.47299674807189],[189.65510255098343,23.950615187162406,-55.56216426561343,-2.9772789638101664,-11.97655179536469,-3.7081028437385033,7.384337972609293,5.040940761964561,3.1509604922446384,2.662823719376978,-2.8427893758566523,-7.056181684386741,-6.192136892606035],[175.46288003213704,21.503150617519474,-53.25227501011244,-6.493723212397822,-21.970610239491286,-11.907455062988728,-3.5085206371572233,8.004755369996975,-4.365710356604317,-10.456922984697666,0.6878756228317848,-5.4583616530371994,-4.701390321733451],[94.05710493854713,10.986088602334364,-36.8425773359097,-0.6202020400841324,-9.716297733382579,-4.070665964547782,6.153486320294905,1.6862524281190292,-1.8494106833481871,-7.061055817001808,-2.1295943532961994,-3.921055341825474,-7.373270719823432],[107.885397400707,7.085577280793136,-47.50943721056687,6.89439984697838,6.341144236745582,1.817151464196414,-0.49898739227524286,-9.448103253001523,-7.333810976880221,-5.274426547522844,-1.4834586493373627,-0.6995371622639457,-0.0870469320629382],[136.8851839825511,12.889653423158057,-37.51394587964225,20.62752642178137,-11.085846341016811,-19.912108469471658,-6.855214691861701,4.138788775234402,0.4201712179018974,-1.3907369030481762,0.4983875541024926,-15.484357158648184,-4.559610093210281],[113.22753800472128,30.2265435053945,-46.677896544776196,-20.30823552502237,1.4830313962673505,-2.248244274240264,-8.465344530318237,-12.395314937639485,-8.553348905218112,7.141353108015711,4.749694180055406,-6.644886192968403,-1.3305373282668456],[135.55295725504402,19.62067880424298,-46.73367336523344,-6.741187917303103,-20.24097182251455,-7.597630767714392,0.6729992201391498,-1.4252591360334188,-4.9498308144143905,-4.965639349265447,9.575336473052682,7.731639002819358,4.471520436861803],[150.25881531101186,21.802064338814798,-44.29274109687284,-4.235352830060731,-17.666143446644888,4.9798391833912055,-1.574840791115542,-6.854201532454216,-1.8143245686618155,-4.957256272327222,-0.6412573771044269,-6.95491703313055,-4.444404538369984],[287.93352127075195,14.194788069449029,-20.8542738190565,-7.562937180414286,-14.707519757324707,-4.513873704074549,1.5940133665028693,3.2558816005724847,-15.335756189096948,-8.504015616224859,-2.413186735930223,5.6268044007364635,6.751816072450436],[115.50524796545506,-8.63073477440851,-37.638063415961064,17.05744845244351,8.81570904694053,-18.08630831742631,-16.395856407749676,3.970968806036655,-2.7254234917680984,8.004075348310908,-0.11591750872191792,-0.5005035625442792,-15.758129772662285],[268.70514822006226,11.058412748164379,-25.46773213645445,7.854306786931966,-14.017515187703342,-14.379088043050848,-21.413163420916916,2.2366286203943497,-1.0553530081388782,-2.3356768880969265,0.10030365029617716,4.147538959725798,-0.42293309969775184],[255.10408759117126,2.9839333802266212,-49.600206177094925,2.7082939644832824,-7.2004856031296836,11.059309271268924,-11.64943878164858,1.1397210888628289,0.36158961127223277,2.231319210013885,-1.730746884878434,-3.321928605224966,-8.447466919033095],[84.14148223493248,15.859740286737232,-22.195860128599918,-23.08961596591042,-11.095667542368714,-12.96894588951639,-1.4930155114211423,12.71942219098584,-1.298092031126412,9.202470233313507,1.1431892806593567,5.054566638857799,-8.850849499582313],[189.9462730884552,2.744879223638137,-7.331325644856165,57.79649490383166,-4.205954299931147,6.58638750953723,-3.205038524907011,10.063949243162675,4.582336877558352,8.01768946256971,-0.6639784855627093,-2.7570988499979228,-2.780719248660455],[87.42413486167789,-7.31100119861122,-43.71139520044253,-4.370058208548226,12.392967006543856,0.6214350981981202,1.8937882920865754,1.6551361395914503,-15.42981498552919,14.031465459019829,2.582221766902142,-7.844305545193481,-4.959824933537969],[232.46439710259438,21.47504436668677,-11.47462630886845,34.90558078823852,-5.769798908648713,22.686108124927827,8.021682382561481,12.994631329051503,-7.937309850940154,0.2743906910653294,-0.892271297543155,-3.5183034088013327,-7.8533489021890395],[78.98907087277621,22.14194057174601,-24.760027972411702,-35.92629877488124,-15.038539366261627,2.176871882838234,10.802981476125487,9.17645733690161,-7.286873817836289,0.6957427358087489,-1.7550364090184347,5.943604438208206,-3.640778614585323],[160.69393920898438,-30.095199207701558,-18.472355549389516,34.71995126326609,-27.004370853267453,9.11287265597757,7.045393126914499,3.7604267549238046,6.553858091793051,-1.1125485854343453,-6.661311261703074,3.242069965782032,-4.029772522354406],[91.09846711484715,10.76485752634354,-24.674128045202018,-19.593985768631658,-10.605193810366648,-8.245362264928461,-9.82994034803978,8.260168962477215,3.5388515978458646,11.526707826582513,5.558638820545692,4.128414292770646,-9.681738221297014],[77.88874008320272,1.2515539060692427,-20.264770688232147,-4.746776937176215,6.941418880911637,-11.34061828070165,-6.192039991198347,11.14066673721247,-6.248096482212682,-1.2284236625980625,-11.006308775083244,11.801742302534763,-8.913770555887485]]"} --------------------------------------------------------------------------------