Repository: stared/interactive-machine-learning-list Branch: master Commit: a2357ca50904 Files: 10 Total size: 88.0 KB Directory structure: gitextract_j6txg211/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── index.html ├── src/ │ ├── main.js │ ├── particles.js │ ├── particlesjs-config.json │ └── style.css └── websites.yaml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [stared] ================================================ FILE: .gitignore ================================================ .idea .config ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Piotr Migdał 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. ================================================ FILE: README.md ================================================ # interactive-machine-learning-list A collaborative list of interactive Machine Learning, Deep Learning and Statistics websites. Started by [Piotr Migdał](https://p.migdal.pl/), but anyone is encouraged to contribute! It is a simple no-build Vue.js website: * [p.migdal.pl/interactive-machine-learning-list/](https://p.migdal.pl/interactive-machine-learning-list/) Feel invited to Pull Request other interactive visualizations (check [websites.yaml](https://github.com/stared/interactive-machine-learning-list/blob/master/websites.yaml))! :) ...aaand if you want to create such visualizations by yourself, see [In Browser AI](https://inbrowser.ai/). ## What goes there? Still I am thinking what is the best criterion. For sure things that are front-end (i.e. JavaScript within browser). For things using backend (when you can see solution, but it uses some PyTorch/TF/etc code on a server) I am still debating, but I lean on being more inclusive. In this context: * make sure it has some didactic value (otherwise ALL services using ML would qualify) * add `backend-dependent` in `uses` Strong preference for open-source solutions (so people can reuse it and learn from code), though it is not a requirement. Though, mention repo and open source license only when it is directly relevant (vs additional materials such as exercises for a book, or Python algorithm). ## Other lists * [Explorable Explanations](http://explorabl.es/) * [Distill](https://distill.pub/) * [Explained Visually](http://setosa.io/ev/) * [AI Experiments with Google](https://experiments.withgoogle.com/collection/ai) ## Inspirations Read [Explorable Explanations](http://worrydream.com/ExplorableExplanations/) by Bret Victor. Inspirations for collecting and displaying content: * [Science-based games - a collaborative list](https://github.com/stared/science-based-games-list) - a list I started (maybe I will turn it int something interactive as well) * [Kaggle Past Solutions](http://ndres.me/kaggle-past-solutions/) - a searchable compilation of Kaggle past solutions * source: [EliotAndres/kaggle-past-solutions](https://github.com/EliotAndres/kaggle-past-solutions) * [D3 Discovery](https://d3-discovery.net/) - finding D3 plugins with ease * source: [https://github.com/wbkd/d3-discovery](https://github.com/wbkd/d3-discovery) ## Design Main layout and styling developed by [Jakub Fogel](https://github.com/fogelkuba) ## TO DO (You are invited to constribute) * Descriptions of sites * Write-up in a different way * Some sorting (alphabetical?) * Share button * Code refactoring :) ================================================ FILE: index.html ================================================ Interactive Machine Learning List
Interactive Machine Learning, Deep Learning and Statistics websites

Open source: see its GitHub repository

It's collaborative: add visualizations via pull requests

If you like it, , if you want to create such visualizations, sign up for In Browser AI!

================================================ FILE: src/main.js ================================================ document.addEventListener('DOMContentLoaded', function () { /* particlesJS.load(@dom-id, @path-json, @callback (optional)); */ particlesJS.load('particles-js', 'src/particlesjs-config.json', function () { console.log('callback - particles.js config loaded'); }); }); Vue.component('tag-selector', { props: ['all-tags'], template: document.getElementById('tag-selector'), }); const app = new Vue({ el: '#app', data: { websites: [], allTags: [], filters: [] }, computed: { filteredWebsites: function() { return this.websites.filter((website) => this.filters.every((tag) => website.allTags.includes(tag) ) ); } }, created: function() { const that = this; fetch("websites.yaml") .then(response => response.text()) .then(text => { that.websites = jsyaml.load(text); that.websites.forEach((website) => { website.allTags = website.tags.concat(website.uses); if (!!website.license) { website.allTags.push(website.license); } }); that.websites.forEach((website) => { website.allTags.forEach((tag) => { if (that.allTags.indexOf(tag) === -1) { that.allTags.push(tag); } }); }); that.allTags.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1 ); }); }, methods: { etAl: (authors) => { if (authors.length < 3) { return authors.join(", "); } else { return authors.slice(0, 3).join(", ") + " et al."; } }, addRemoveToFilters: function(tag) { if (this.filters.indexOf(tag) === -1) { this.filters.push(tag); } else { this.filters = this.filters .filter((x) => x !== tag); } }, checkActive: function(tag) { return this.filters.indexOf(tag) > -1; }, clearAllFilters: function() { this.filters = []; }, getTagCount: function(tag) { return this.filteredWebsites .filter((website) => website.allTags.includes(tag)) .length; } } }); ================================================ FILE: src/particles.js ================================================ /* ----------------------------------------------- /* Author : Vincent Garreau - vincentgarreau.com /* MIT license: http://opensource.org/licenses/MIT /* Demo / Generator : vincentgarreau.com/particles.js /* GitHub : github.com/VincentGarreau/particles.js /* How to use? : Check the GitHub README /* v2.0.0 /* ----------------------------------------------- */ var pJS = function(tag_id, params){ var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el'); /* particles.js variables with default values */ this.pJS = { canvas: { el: canvas_el, w: canvas_el.offsetWidth, h: canvas_el.offsetHeight }, particles: { number: { value: 400, density: { enable: true, value_area: 800 } }, color: { value: '#fff' }, shape: { type: 'circle', stroke: { width: 0, color: '#ff0000' }, polygon: { nb_sides: 5 }, image: { src: '', width: 100, height: 100 } }, opacity: { value: 1, random: false, anim: { enable: false, speed: 2, opacity_min: 0, sync: false } }, size: { value: 20, random: false, anim: { enable: false, speed: 20, size_min: 0, sync: false } }, line_linked: { enable: true, distance: 100, color: '#fff', opacity: 1, width: 1 }, move: { enable: true, speed: 2, direction: 'none', random: false, straight: false, out_mode: 'out', bounce: false, attract: { enable: false, rotateX: 3000, rotateY: 3000 } }, array: [] }, interactivity: { detect_on: 'canvas', events: { onhover: { enable: true, mode: 'grab' }, onclick: { enable: true, mode: 'push' }, resize: true }, modes: { grab:{ distance: 100, line_linked:{ opacity: 1 } }, bubble:{ distance: 200, size: 80, duration: 0.4 }, repulse:{ distance: 200, duration: 0.4 }, push:{ particles_nb: 4 }, remove:{ particles_nb: 2 } }, mouse:{} }, retina_detect: false, fn: { interact: {}, modes: {}, vendors:{} }, tmp: {} }; var pJS = this.pJS; /* params settings */ if(params){ Object.deepExtend(pJS, params); } pJS.tmp.obj = { size_value: pJS.particles.size.value, size_anim_speed: pJS.particles.size.anim.speed, move_speed: pJS.particles.move.speed, line_linked_distance: pJS.particles.line_linked.distance, line_linked_width: pJS.particles.line_linked.width, mode_grab_distance: pJS.interactivity.modes.grab.distance, mode_bubble_distance: pJS.interactivity.modes.bubble.distance, mode_bubble_size: pJS.interactivity.modes.bubble.size, mode_repulse_distance: pJS.interactivity.modes.repulse.distance }; pJS.fn.retinaInit = function(){ if(pJS.retina_detect && window.devicePixelRatio > 1){ pJS.canvas.pxratio = window.devicePixelRatio; pJS.tmp.retina = true; } else{ pJS.canvas.pxratio = 1; pJS.tmp.retina = false; } pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio; pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio; pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio; pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio; pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio; pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio; pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio; pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio; pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio; pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio; pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio; }; /* ---------- pJS functions - canvas ------------ */ pJS.fn.canvasInit = function(){ pJS.canvas.ctx = pJS.canvas.el.getContext('2d'); }; pJS.fn.canvasSize = function(){ pJS.canvas.el.width = pJS.canvas.w; pJS.canvas.el.height = pJS.canvas.h; if(pJS && pJS.interactivity.events.resize){ window.addEventListener('resize', function(){ pJS.canvas.w = pJS.canvas.el.offsetWidth; pJS.canvas.h = pJS.canvas.el.offsetHeight; /* resize canvas */ if(pJS.tmp.retina){ pJS.canvas.w *= pJS.canvas.pxratio; pJS.canvas.h *= pJS.canvas.pxratio; } pJS.canvas.el.width = pJS.canvas.w; pJS.canvas.el.height = pJS.canvas.h; /* repaint canvas on anim disabled */ if(!pJS.particles.move.enable){ pJS.fn.particlesEmpty(); pJS.fn.particlesCreate(); pJS.fn.particlesDraw(); pJS.fn.vendors.densityAutoParticles(); } /* density particles enabled */ pJS.fn.vendors.densityAutoParticles(); }); } }; pJS.fn.canvasPaint = function(){ pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h); }; pJS.fn.canvasClear = function(){ pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); }; /* --------- pJS functions - particles ----------- */ pJS.fn.particle = function(color, opacity, position){ /* size */ this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value; if(pJS.particles.size.anim.enable){ this.size_status = false; this.vs = pJS.particles.size.anim.speed / 100; if(!pJS.particles.size.anim.sync){ this.vs = this.vs * Math.random(); } } /* position */ this.x = position ? position.x : Math.random() * pJS.canvas.w; this.y = position ? position.y : Math.random() * pJS.canvas.h; /* check position - into the canvas */ if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius; else if(this.x < this.radius*2) this.x = this.x + this.radius; if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius; else if(this.y < this.radius*2) this.y = this.y + this.radius; /* check position - avoid overlap */ if(pJS.particles.move.bounce){ pJS.fn.vendors.checkOverlap(this, position); } /* color */ this.color = {}; if(typeof(color.value) == 'object'){ if(color.value instanceof Array){ var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)]; this.color.rgb = hexToRgb(color_selected); }else{ if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){ this.color.rgb = { r: color.value.r, g: color.value.g, b: color.value.b } } if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){ this.color.hsl = { h: color.value.h, s: color.value.s, l: color.value.l } } } } else if(color.value == 'random'){ this.color.rgb = { r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0), g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0), b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0) } } else if(typeof(color.value) == 'string'){ this.color = color; this.color.rgb = hexToRgb(this.color.value); } /* opacity */ this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value; if(pJS.particles.opacity.anim.enable){ this.opacity_status = false; this.vo = pJS.particles.opacity.anim.speed / 100; if(!pJS.particles.opacity.anim.sync){ this.vo = this.vo * Math.random(); } } /* animation - velocity for speed */ var velbase = {} switch(pJS.particles.move.direction){ case 'top': velbase = { x:0, y:-1 }; break; case 'top-right': velbase = { x:0.5, y:-0.5 }; break; case 'right': velbase = { x:1, y:-0 }; break; case 'bottom-right': velbase = { x:0.5, y:0.5 }; break; case 'bottom': velbase = { x:0, y:1 }; break; case 'bottom-left': velbase = { x:-0.5, y:1 }; break; case 'left': velbase = { x:-1, y:0 }; break; case 'top-left': velbase = { x:-0.5, y:-0.5 }; break; default: velbase = { x:0, y:0 }; break; } if(pJS.particles.move.straight){ this.vx = velbase.x; this.vy = velbase.y; if(pJS.particles.move.random){ this.vx = this.vx * (Math.random()); this.vy = this.vy * (Math.random()); } }else{ this.vx = velbase.x + Math.random()-0.5; this.vy = velbase.y + Math.random()-0.5; } // var theta = 2.0 * Math.PI * Math.random(); // this.vx = Math.cos(theta); // this.vy = Math.sin(theta); this.vx_i = this.vx; this.vy_i = this.vy; /* if shape is image */ var shape_type = pJS.particles.shape.type; if(typeof(shape_type) == 'object'){ if(shape_type instanceof Array){ var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)]; this.shape = shape_selected; } }else{ this.shape = shape_type; } if(this.shape == 'image'){ var sh = pJS.particles.shape; this.img = { src: sh.image.src, ratio: sh.image.width / sh.image.height } if(!this.img.ratio) this.img.ratio = 1; if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){ pJS.fn.vendors.createSvgImg(this); if(pJS.tmp.pushing){ this.img.loaded = false; } } } }; pJS.fn.particle.prototype.draw = function() { var p = this; if(p.radius_bubble != undefined){ var radius = p.radius_bubble; }else{ var radius = p.radius; } if(p.opacity_bubble != undefined){ var opacity = p.opacity_bubble; }else{ var opacity = p.opacity; } if(p.color.rgb){ var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')'; }else{ var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')'; } pJS.canvas.ctx.fillStyle = color_value; pJS.canvas.ctx.beginPath(); switch(p.shape){ case 'circle': pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false); break; case 'edge': pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2); break; case 'triangle': pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2); break; case 'polygon': pJS.fn.vendors.drawShape( pJS.canvas.ctx, p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX p.y - radius / (2.66/3.5), // startY radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength pJS.particles.shape.polygon.nb_sides, // sideCountNumerator 1 // sideCountDenominator ); break; case 'star': pJS.fn.vendors.drawShape( pJS.canvas.ctx, p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX p.y - radius / (2*2.66/3.5), // startY radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength pJS.particles.shape.polygon.nb_sides, // sideCountNumerator 2 // sideCountDenominator ); break; case 'image': function draw(){ pJS.canvas.ctx.drawImage( img_obj, p.x-radius, p.y-radius, radius*2, radius*2 / p.img.ratio ); } if(pJS.tmp.img_type == 'svg'){ var img_obj = p.img.obj; }else{ var img_obj = pJS.tmp.img_obj; } if(img_obj){ draw(); } break; } pJS.canvas.ctx.closePath(); if(pJS.particles.shape.stroke.width > 0){ pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color; pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width; pJS.canvas.ctx.stroke(); } pJS.canvas.ctx.fill(); }; pJS.fn.particlesCreate = function(){ for(var i = 0; i < pJS.particles.number.value; i++) { pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value)); } }; pJS.fn.particlesUpdate = function(){ for(var i = 0; i < pJS.particles.array.length; i++){ /* the particle */ var p = pJS.particles.array[i]; // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy; // var f = -BANG_SIZE / d; // if ( d < BANG_SIZE ) { // var t = Math.atan2( dy, dx ); // p.vx = f * Math.cos(t); // p.vy = f * Math.sin(t); // } /* move the particle */ if(pJS.particles.move.enable){ var ms = pJS.particles.move.speed/2; p.x += p.vx * ms; p.y += p.vy * ms; } /* change opacity status */ if(pJS.particles.opacity.anim.enable) { if(p.opacity_status == true) { if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false; p.opacity += p.vo; }else { if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true; p.opacity -= p.vo; } if(p.opacity < 0) p.opacity = 0; } /* change size */ if(pJS.particles.size.anim.enable){ if(p.size_status == true){ if(p.radius >= pJS.particles.size.value) p.size_status = false; p.radius += p.vs; }else{ if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true; p.radius -= p.vs; } if(p.radius < 0) p.radius = 0; } /* change particle position if it is out of canvas */ if(pJS.particles.move.out_mode == 'bounce'){ var new_pos = { x_left: p.radius, x_right: pJS.canvas.w, y_top: p.radius, y_bottom: pJS.canvas.h } }else{ var new_pos = { x_left: -p.radius, x_right: pJS.canvas.w + p.radius, y_top: -p.radius, y_bottom: pJS.canvas.h + p.radius } } if(p.x - p.radius > pJS.canvas.w){ p.x = new_pos.x_left; p.y = Math.random() * pJS.canvas.h; } else if(p.x + p.radius < 0){ p.x = new_pos.x_right; p.y = Math.random() * pJS.canvas.h; } if(p.y - p.radius > pJS.canvas.h){ p.y = new_pos.y_top; p.x = Math.random() * pJS.canvas.w; } else if(p.y + p.radius < 0){ p.y = new_pos.y_bottom; p.x = Math.random() * pJS.canvas.w; } /* out of canvas modes */ switch(pJS.particles.move.out_mode){ case 'bounce': if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx; else if (p.x - p.radius < 0) p.vx = -p.vx; if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy; else if (p.y - p.radius < 0) p.vy = -p.vy; break; } /* events */ if(isInArray('grab', pJS.interactivity.events.onhover.mode)){ pJS.fn.modes.grabParticle(p); } if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){ pJS.fn.modes.bubbleParticle(p); } if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){ pJS.fn.modes.repulseParticle(p); } /* interaction auto between particles */ if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){ for(var j = i + 1; j < pJS.particles.array.length; j++){ var p2 = pJS.particles.array[j]; /* link particles */ if(pJS.particles.line_linked.enable){ pJS.fn.interact.linkParticles(p,p2); } /* attract particles */ if(pJS.particles.move.attract.enable){ pJS.fn.interact.attractParticles(p,p2); } /* bounce particles */ if(pJS.particles.move.bounce){ pJS.fn.interact.bounceParticles(p,p2); } } } } }; pJS.fn.particlesDraw = function(){ /* clear canvas */ pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h); /* update each particles param */ pJS.fn.particlesUpdate(); /* draw each particle */ for(var i = 0; i < pJS.particles.array.length; i++){ var p = pJS.particles.array[i]; p.draw(); } }; pJS.fn.particlesEmpty = function(){ pJS.particles.array = []; }; pJS.fn.particlesRefresh = function(){ /* init all */ cancelRequestAnimFrame(pJS.fn.checkAnimFrame); cancelRequestAnimFrame(pJS.fn.drawAnimFrame); pJS.tmp.source_svg = undefined; pJS.tmp.img_obj = undefined; pJS.tmp.count_svg = 0; pJS.fn.particlesEmpty(); pJS.fn.canvasClear(); /* restart */ pJS.fn.vendors.start(); }; /* ---------- pJS functions - particles interaction ------------ */ pJS.fn.interact.linkParticles = function(p1, p2){ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = Math.sqrt(dx*dx + dy*dy); /* draw a line between p1 and p2 if the distance between them is under the config distance */ if(dist <= pJS.particles.line_linked.distance){ var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance; if(opacity_line > 0){ /* style */ var color_line = pJS.particles.line_linked.color_rgb_line; pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')'; pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ /* path */ pJS.canvas.ctx.beginPath(); pJS.canvas.ctx.moveTo(p1.x, p1.y); pJS.canvas.ctx.lineTo(p2.x, p2.y); pJS.canvas.ctx.stroke(); pJS.canvas.ctx.closePath(); } } }; pJS.fn.interact.attractParticles = function(p1, p2){ /* condensed particles */ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = Math.sqrt(dx*dx + dy*dy); if(dist <= pJS.particles.line_linked.distance){ var ax = dx/(pJS.particles.move.attract.rotateX*1000), ay = dy/(pJS.particles.move.attract.rotateY*1000); p1.vx -= ax; p1.vy -= ay; p2.vx += ax; p2.vy += ay; } } pJS.fn.interact.bounceParticles = function(p1, p2){ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = Math.sqrt(dx*dx + dy*dy), dist_p = p1.radius+p2.radius; if(dist <= dist_p){ p1.vx = -p1.vx; p1.vy = -p1.vy; p2.vx = -p2.vx; p2.vy = -p2.vy; } } /* ---------- pJS functions - modes events ------------ */ pJS.fn.modes.pushParticles = function(nb, pos){ pJS.tmp.pushing = true; for(var i = 0; i < nb; i++){ pJS.particles.array.push( new pJS.fn.particle( pJS.particles.color, pJS.particles.opacity.value, { 'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w, 'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h } ) ) if(i == nb-1){ if(!pJS.particles.move.enable){ pJS.fn.particlesDraw(); } pJS.tmp.pushing = false; } } }; pJS.fn.modes.removeParticles = function(nb){ pJS.particles.array.splice(0, nb); if(!pJS.particles.move.enable){ pJS.fn.particlesDraw(); } }; pJS.fn.modes.bubbleParticle = function(p){ /* on hover event */ if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){ var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, dy_mouse = p.y - pJS.interactivity.mouse.pos_y, dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse), ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance; function init(){ p.opacity_bubble = p.opacity; p.radius_bubble = p.radius; } /* mousemove - check ratio */ if(dist_mouse <= pJS.interactivity.modes.bubble.distance){ if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){ /* size */ if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){ if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){ var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio); if(size >= 0){ p.radius_bubble = size; } }else{ var dif = p.radius - pJS.interactivity.modes.bubble.size, size = p.radius - (dif*ratio); if(size > 0){ p.radius_bubble = size; }else{ p.radius_bubble = 0; } } } /* opacity */ if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){ if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){ var opacity = pJS.interactivity.modes.bubble.opacity*ratio; if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){ p.opacity_bubble = opacity; } }else{ var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio; if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){ p.opacity_bubble = opacity; } } } } }else{ init(); } /* mouseleave */ if(pJS.interactivity.status == 'mouseleave'){ init(); } } /* on click event */ else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){ if(pJS.tmp.bubble_clicking){ var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x, dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y, dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse), time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000; if(time_spent > pJS.interactivity.modes.bubble.duration){ pJS.tmp.bubble_duration_end = true; } if(time_spent > pJS.interactivity.modes.bubble.duration*2){ pJS.tmp.bubble_clicking = false; pJS.tmp.bubble_duration_end = false; } } function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){ if(bubble_param != particles_param){ if(!pJS.tmp.bubble_duration_end){ if(dist_mouse <= pJS.interactivity.modes.bubble.distance){ if(p_obj_bubble != undefined) var obj = p_obj_bubble; else var obj = p_obj; if(obj != bubble_param){ var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration); if(id == 'size') p.radius_bubble = value; if(id == 'opacity') p.opacity_bubble = value; } }else{ if(id == 'size') p.radius_bubble = undefined; if(id == 'opacity') p.opacity_bubble = undefined; } }else{ if(p_obj_bubble != undefined){ var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration), dif = bubble_param - value_tmp; value = bubble_param + dif; if(id == 'size') p.radius_bubble = value; if(id == 'opacity') p.opacity_bubble = value; } } } } if(pJS.tmp.bubble_clicking){ /* size */ process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size'); /* opacity */ process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity'); } } }; pJS.fn.modes.repulseParticle = function(p){ if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') { var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, dy_mouse = p.y - pJS.interactivity.mouse.pos_y, dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse); var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse}, repulseRadius = pJS.interactivity.modes.repulse.distance, velocity = 100, repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50); var pos = { x: p.x + normVec.x * repulseFactor, y: p.y + normVec.y * repulseFactor } if(pJS.particles.move.out_mode == 'bounce'){ if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x; if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y; }else{ p.x = pos.x; p.y = pos.y; } } else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) { if(!pJS.tmp.repulse_finish){ pJS.tmp.repulse_count++; if(pJS.tmp.repulse_count == pJS.particles.array.length){ pJS.tmp.repulse_finish = true; } } if(pJS.tmp.repulse_clicking){ var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3); var dx = pJS.interactivity.mouse.click_pos_x - p.x, dy = pJS.interactivity.mouse.click_pos_y - p.y, d = dx*dx + dy*dy; var force = -repulseRadius / d * 1; function process(){ var f = Math.atan2(dy,dx); p.vx = force * Math.cos(f); p.vy = force * Math.sin(f); if(pJS.particles.move.out_mode == 'bounce'){ var pos = { x: p.x + p.vx, y: p.y + p.vy } if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx; else if (pos.x - p.radius < 0) p.vx = -p.vx; if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy; else if (pos.y - p.radius < 0) p.vy = -p.vy; } } // default if(d <= repulseRadius){ process(); } // bang - slow motion mode // if(!pJS.tmp.repulse_finish){ // if(d <= repulseRadius){ // process(); // } // }else{ // process(); // } }else{ if(pJS.tmp.repulse_clicking == false){ p.vx = p.vx_i; p.vy = p.vy_i; } } } } pJS.fn.modes.grabParticle = function(p){ if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){ var dx_mouse = p.x - pJS.interactivity.mouse.pos_x, dy_mouse = p.y - pJS.interactivity.mouse.pos_y, dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse); /* draw a line between the cursor and the particle if the distance between them is under the config distance */ if(dist_mouse <= pJS.interactivity.modes.grab.distance){ var opacity_line = pJS.interactivity.modes.grab.line_linked.opacity - (dist_mouse / (1/pJS.interactivity.modes.grab.line_linked.opacity)) / pJS.interactivity.modes.grab.distance; if(opacity_line > 0){ /* style */ var color_line = pJS.particles.line_linked.color_rgb_line; pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')'; pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width; //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */ /* path */ pJS.canvas.ctx.beginPath(); pJS.canvas.ctx.moveTo(p.x, p.y); pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y); pJS.canvas.ctx.stroke(); pJS.canvas.ctx.closePath(); } } } }; /* ---------- pJS functions - vendors ------------ */ pJS.fn.vendors.eventsListeners = function(){ /* events target element */ if(pJS.interactivity.detect_on == 'window'){ pJS.interactivity.el = window; }else{ pJS.interactivity.el = pJS.canvas.el; } /* detect mouse pos - on hover / click event */ if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){ /* el on mousemove */ pJS.interactivity.el.addEventListener('mousemove', function(e){ if(pJS.interactivity.el == window){ var pos_x = e.clientX, pos_y = e.clientY; } else{ var pos_x = e.offsetX || e.clientX, pos_y = e.offsetY || e.clientY; } pJS.interactivity.mouse.pos_x = pos_x; pJS.interactivity.mouse.pos_y = pos_y; if(pJS.tmp.retina){ pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio; pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio; } pJS.interactivity.status = 'mousemove'; }); /* el on onmouseleave */ pJS.interactivity.el.addEventListener('mouseleave', function(e){ pJS.interactivity.mouse.pos_x = null; pJS.interactivity.mouse.pos_y = null; pJS.interactivity.status = 'mouseleave'; }); } /* on click event */ if(pJS.interactivity.events.onclick.enable){ pJS.interactivity.el.addEventListener('click', function(){ pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x; pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y; pJS.interactivity.mouse.click_time = new Date().getTime(); if(pJS.interactivity.events.onclick.enable){ switch(pJS.interactivity.events.onclick.mode){ case 'push': if(pJS.particles.move.enable){ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse); }else{ if(pJS.interactivity.modes.push.particles_nb == 1){ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse); } else if(pJS.interactivity.modes.push.particles_nb > 1){ pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb); } } break; case 'remove': pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb); break; case 'bubble': pJS.tmp.bubble_clicking = true; break; case 'repulse': pJS.tmp.repulse_clicking = true; pJS.tmp.repulse_count = 0; pJS.tmp.repulse_finish = false; setTimeout(function(){ pJS.tmp.repulse_clicking = false; }, pJS.interactivity.modes.repulse.duration*1000) break; } } }); } }; pJS.fn.vendors.densityAutoParticles = function(){ if(pJS.particles.number.density.enable){ /* calc area */ var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000; if(pJS.tmp.retina){ area = area/(pJS.canvas.pxratio*2); } /* calc number of particles based on density area */ var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area; /* add or remove X particles */ var missing_particles = pJS.particles.array.length - nb_particles; if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles)); else pJS.fn.modes.removeParticles(missing_particles); } }; pJS.fn.vendors.checkOverlap = function(p1, position){ for(var i = 0; i < pJS.particles.array.length; i++){ var p2 = pJS.particles.array[i]; var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = Math.sqrt(dx*dx + dy*dy); if(dist <= p1.radius + p2.radius){ p1.x = position ? position.x : Math.random() * pJS.canvas.w; p1.y = position ? position.y : Math.random() * pJS.canvas.h; pJS.fn.vendors.checkOverlap(p1); } } }; pJS.fn.vendors.createSvgImg = function(p){ /* set color to svg element */ var svgXml = pJS.tmp.source_svg, rgbHex = /#([0-9A-F]{3,6})/gi, coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) { if(p.color.rgb){ var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')'; }else{ var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')'; } return color_value; }); /* prepare to create img with colored svg */ var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}), DOMURL = window.URL || window.webkitURL || window, url = DOMURL.createObjectURL(svg); /* create particle img obj */ var img = new Image(); img.addEventListener('load', function(){ p.img.obj = img; p.img.loaded = true; DOMURL.revokeObjectURL(url); pJS.tmp.count_svg++; }); img.src = url; }; pJS.fn.vendors.destroypJS = function(){ cancelAnimationFrame(pJS.fn.drawAnimFrame); canvas_el.remove(); pJSDom = null; }; pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){ // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/ var sideCount = sideCountNumerator * sideCountDenominator; var decimalSides = sideCountNumerator / sideCountDenominator; var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides; var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians c.save(); c.beginPath(); c.translate(startX, startY); c.moveTo(0,0); for (var i = 0; i < sideCount; i++) { c.lineTo(sideLength,0); c.translate(sideLength,0); c.rotate(interiorAngle); } //c.stroke(); c.fill(); c.restore(); }; pJS.fn.vendors.exportImg = function(){ window.open(pJS.canvas.el.toDataURL('image/png'), '_blank'); }; pJS.fn.vendors.loadImg = function(type){ pJS.tmp.img_error = undefined; if(pJS.particles.shape.image.src != ''){ if(type == 'svg'){ var xhr = new XMLHttpRequest(); xhr.open('GET', pJS.particles.shape.image.src); xhr.onreadystatechange = function (data) { if(xhr.readyState == 4){ if(xhr.status == 200){ pJS.tmp.source_svg = data.currentTarget.response; pJS.fn.vendors.checkBeforeDraw(); }else{ console.log('Error pJS - Image not found'); pJS.tmp.img_error = true; } } } xhr.send(); }else{ var img = new Image(); img.addEventListener('load', function(){ pJS.tmp.img_obj = img; pJS.fn.vendors.checkBeforeDraw(); }); img.src = pJS.particles.shape.image.src; } }else{ console.log('Error pJS - No image.src'); pJS.tmp.img_error = true; } }; pJS.fn.vendors.draw = function(){ if(pJS.particles.shape.type == 'image'){ if(pJS.tmp.img_type == 'svg'){ if(pJS.tmp.count_svg >= pJS.particles.number.value){ pJS.fn.particlesDraw(); if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); }else{ //console.log('still loading...'); if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); } }else{ if(pJS.tmp.img_obj != undefined){ pJS.fn.particlesDraw(); if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); }else{ if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); } } }else{ pJS.fn.particlesDraw(); if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame); else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw); } }; pJS.fn.vendors.checkBeforeDraw = function(){ // if shape is image if(pJS.particles.shape.type == 'image'){ if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){ pJS.tmp.checkAnimFrame = requestAnimFrame(check); }else{ //console.log('images loaded! cancel check'); cancelRequestAnimFrame(pJS.tmp.checkAnimFrame); if(!pJS.tmp.img_error){ pJS.fn.vendors.init(); pJS.fn.vendors.draw(); } } }else{ pJS.fn.vendors.init(); pJS.fn.vendors.draw(); } }; pJS.fn.vendors.init = function(){ /* init canvas + particles */ pJS.fn.retinaInit(); pJS.fn.canvasInit(); pJS.fn.canvasSize(); pJS.fn.canvasPaint(); pJS.fn.particlesCreate(); pJS.fn.vendors.densityAutoParticles(); /* particles.line_linked - convert hex colors to rgb */ pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color); }; pJS.fn.vendors.start = function(){ if(isInArray('image', pJS.particles.shape.type)){ pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3); pJS.fn.vendors.loadImg(pJS.tmp.img_type); }else{ pJS.fn.vendors.checkBeforeDraw(); } }; /* ---------- pJS - start ------------ */ pJS.fn.vendors.eventsListeners(); pJS.fn.vendors.start(); }; /* ---------- global functions - vendors ------------ */ Object.deepExtend = function(destination, source) { for (var property in source) { if (source[property] && source[property].constructor && source[property].constructor === Object) { destination[property] = destination[property] || {}; arguments.callee(destination[property], source[property]); } else { destination[property] = source[property]; } } return destination; }; window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback){ window.setTimeout(callback, 1000 / 60); }; })(); window.cancelRequestAnimFrame = ( function() { return window.cancelAnimationFrame || window.webkitCancelRequestAnimationFrame || window.mozCancelRequestAnimationFrame || window.oCancelRequestAnimationFrame || window.msCancelRequestAnimationFrame || clearTimeout } )(); function hexToRgb(hex){ // By Tim Down - http://stackoverflow.com/a/5624139/3493650 // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; function clamp(number, min, max) { return Math.min(Math.max(number, min), max); }; function isInArray(value, array) { return array.indexOf(value) > -1; } /* ---------- particles.js functions - start ------------ */ window.pJSDom = []; window.particlesJS = function(tag_id, params){ //console.log(params); /* no string id? so it's object params, and set the id with default id */ if(typeof(tag_id) != 'string'){ params = tag_id; tag_id = 'particles-js'; } /* no id? set the id to default id */ if(!tag_id){ tag_id = 'particles-js'; } /* pJS elements */ var pJS_tag = document.getElementById(tag_id), pJS_canvas_class = 'particles-js-canvas-el', exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class); /* remove canvas if exists into the pJS target tag */ if(exist_canvas.length){ while(exist_canvas.length > 0){ pJS_tag.removeChild(exist_canvas[0]); } } /* create canvas element */ var canvas_el = document.createElement('canvas'); canvas_el.className = pJS_canvas_class; /* set size canvas */ canvas_el.style.width = "100%"; canvas_el.style.height = "100%"; /* append canvas */ var canvas = document.getElementById(tag_id).appendChild(canvas_el); /* launch particle.js */ if(canvas != null){ pJSDom.push(new pJS(tag_id, params)); } }; window.particlesJS.load = function(tag_id, path_config_json, callback){ /* load json config */ var xhr = new XMLHttpRequest(); xhr.open('GET', path_config_json); xhr.onreadystatechange = function (data) { if(xhr.readyState == 4){ if(xhr.status == 200){ var params = JSON.parse(data.currentTarget.response); window.particlesJS(tag_id, params); if(callback) callback(); }else{ console.log('Error pJS - XMLHttpRequest status: '+xhr.status); console.log('Error pJS - File config not found'); } } }; xhr.send(); }; ================================================ FILE: src/particlesjs-config.json ================================================ { "particles": { "number": { "value": 30, "density": { "enable": true, "value_area": 500 } }, "color": { "value": "#a6387b" }, "shape": { "type": "circle", "stroke": { "width": 0, "color": "#000000" }, "polygon": { "nb_sides": 5 }, "image": { "src": "img/github.svg", "width": 100, "height": 100 } }, "opacity": { "value": 0.5, "random": false, "anim": { "enable": false, "speed": 1, "opacity_min": 0.1, "sync": false } }, "size": { "value": 3, "random": true, "anim": { "enable": false, "speed": 5, "size_min": 0.1, "sync": false } }, "line_linked": { "enable": true, "distance": 150, "color": "#a6387b", "opacity": 0.1008530152163807, "width": 0.5 }, "move": { "enable": true, "speed": 2, "direction": "none", "random": false, "straight": false, "out_mode": "out", "bounce": false, "attract": { "enable": false, "rotateX": 600, "rotateY": 1200 } } }, "interactivity": { "detect_on": "window", "events": { "onhover": { "enable": true, "mode": "grab" }, "onclick": { "enable": false, "mode": "push" }, "resize": true }, "modes": { "grab": { "distance": 400, "line_linked": { "opacity": 1 } }, "bubble": { "distance": 400, "size": 40, "duration": 2, "opacity": 8, "speed": 3 }, "repulse": { "distance": 200, "duration": 0.4 }, "push": { "particles_nb": 4 }, "remove": { "particles_nb": 2 } } }, "retina_detect": true } ================================================ FILE: src/style.css ================================================ * { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } html { } body { background: rgba(0, 0, 0, .1); } h2 { color: #555; font-weight: bold; } a { /*margin-right: .5rem;*/ text-decoration: none; cursor: pointer; } a:hover { text-decoration: underline; } ul > li { list-style: none; } .mdl-layout__container { top: 0; } .mdl-navigation { padding: 0 1rem; } .page-content > .mdl-grid { padding-top: 3rem; } .mdl-cell { /*overflow-y: auto;*/ } .mdl-card { min-height: 240px; margin: 0 auto; width: 100%; position: relative; z-index: 2; background-size: cover; overflow: hidden; } .mdl-card__title { max-width: 80%; padding-bottom: 0; } .mdl-card__title > h3 { min-height: 60px; display: inline-block; font-weight: bold; color: white; position: relative; z-index: 2; text-shadow: 0 0 5px black; } .mdl-card__supporting-text { color: #fff; padding: 0 1rem; position: relative; z-index: 2; } .mdl-card__supporting-text > span { padding-top: .5rem; } .mdl-card__supporting-text > span.authors { text-shadow: 0 0 5px black; } .mdl-card__supporting-text .tag { margin-bottom: .5rem; margin-right: .5rem; } .mdl-card__supporting-text i.material-icons { font-size: 20px; position: relative; top: 0.35rem; } .mdl-card__supporting-text > .mdl-grid { padding: 0; } .mdl-card--bottom { bottom: 0; left: 0; min-height: 75px; padding-top: 1rem !important; position: absolute; } .mdl-card__menu { background: white; border-radius: 2rem; } .mdl-card__actions { background: rgba(0,0,0, 0.5); font-size: 14px; padding: .5rem 1rem; color: white; } .background { background-color: hsla(0,0%,0%,0.70); background-blend-mode: overlay; min-height: 300px; padding-bottom: 75px; background-size: cover; color: #fff; position: relative; overflow: hidden; } .background:before { content: ''; left: 0; top: 0; z-index: 0; position: absolute; width: 100%; height: 100%; background: linear-gradient(to bottom right, rgba(0,0,0,.1), transparent); } .right { text-align: right; } .particles-js-canvas-el { position: fixed; pointer-events: none; top: 0; left: 0; z-index: -1; } .illustration { background: url(./illustration.svg); min-width: 100%; height: 100%; max-height: 370px; background-repeat: no-repeat; background-position-x: right; } .tags-wrapper { margin: 2rem auto; max-width: 70vw; position: relative; z-index: 3; text-align: left; } .tags-wrapper > .tag { margin-bottom: .5rem; margin-right: .5rem; } .tag { font-weight: bold; display: inline-block; background: rgba(255, 255, 255, 1); border: 1px solid rgb(255, 64, 129); color: rgb(255, 64, 129); padding: 0.15rem .75rem; border-radius: 1rem; text-decoration: none; font-size: 11px; white-space: nowrap; margin-bottom: .5rem; } .tag:hover { color: white; background: rgba(255, 64, 129, 0.75); text-decoration: none; } .tag.active { color: white; background: rgb(255, 64, 129); text-decoration: none; } .delete { position: relative; top: .25rem; font-size: 16px; } footer { position: relative; z-index: 3; /*margin-top: 3rem;*/ } @media screen and (max-width: 840px){ .illustration{ height: 150px; width: 100%; background-position-x: center; } } @media screen and (max-width: 505px){ .illustration{ height: 150px; background-position-x: center; } .mdl-layout__header-row{ padding: 0 16px; } .mdl-layout-title{ text-align: left; } h2{ font-size: 30px; line-height: 30px; } .tags-wrapper{ text-align: center; } .mdl-mini-footer--link-list, .mdl-mini-footer__link-list{ flex-direction: column; } .mdl-mini-footer--link-list, .mdl-mini-footer__link-list > li{ margin-bottom: 1rem; } footer.ul>li{ display: block; } } ================================================ FILE: websites.yaml ================================================ - name: "CNN Explainer" desc: "An interactive visualization system designed to help non-experts learn about Convolutional Neural Networks (CNNs)" url: https://poloclub.github.io/cnn-explainer/ repo: https://github.com/poloclub/cnn-explainer license: MIT writeup: https://arxiv.org/abs/2004.15004 authors: [Zijie Jay Wang, Robert Turko, Omar Shaikh, Haekyu Park, Nilaksh Das, Fred Hohman, Minsuk Kahng, Duen Horng (Polo) Chau] tags: [cnn, convnet] uses: [demo, tutorial] img: https://i.imgur.com/7FGSR7S.png - name: "Deep Recurrent Nets character generation demo" desc: "This demo shows usage of the recurrentjs library train RNN and LSTM networks in JavaScript." url: https://cs.stanford.edu/people/karpathy/recurrentjs/ repo: https://github.com/karpathy/recurrentjs license: MIT writeup: http://karpathy.github.io/2015/05/21/rnn-effectiveness/ authors: [Andrej Karpathy] tags: [rnn, lstm, nlp] uses: [demo, library] img: https://cs.stanford.edu/people/karpathy/recurrentjs/eg.png - name: "ConvNetJS" desc: "Various demos of deep learning in JavaScript, using ConvNetJS library." url: https://cs.stanford.edu/people/karpathy/convnetjs/ repo: https://github.com/karpathy/convnetjs license: MIT writeup: "" authors: [Andrej Karpathy] tags: [convnet] uses: [demo, library] img: https://cs.stanford.edu/people/karpathy/convnetjs/cifar10.jpeg - name: "Support Vector Machine in JavaScript" desc: "A lightweight implementation of the sequential minimal optimization algorithm to train a binary SVM model in JavaScript." url: https://cs.stanford.edu/~karpathy/svmjs/demo/ repo: https://github.com/karpathy/svmjs license: MIT writeup: "" authors: [Andrej Karpathy] tags: [svm] uses: [demo, library, npm] img: imgs/svmjs.png - name: "tSNEJS demo" desc: "tSNE used for clustering the 500 most-followed accounts on Twitter." url: https://cs.stanford.edu/people/karpathy/tsnejs/ repo: https://github.com/karpathy/tsnejs license: MIT writeup: http://karpathy.github.io/2014/07/02/visualizing-top-tweeps-with-t-sne-in-Javascript/ authors: [Andrej Karpathy] tags: [tsne] uses: [demo, library] img: https://karpathy.github.io/assets/tsne_preview.jpeg - name: "Random Forest demo in JavaScript" desc: "Random Forest implementation for JavaScript. Supports arbitrary weak learners." url: https://cs.stanford.edu/~karpathy/svmjs/demo/demoforest.html repo: https://github.com/karpathy/forestjs license: MIT writeup: "" authors: [Andrej Karpathy] tags: [tsne] uses: [demo, library] img: imgs/forestjs.png - name: "Keras.js" desc: "Keras.js is a library enabling running Keras models in the browser, with GPU support provided by WebGL 2." url: https://transcranial.github.io/keras-js/#/ repo: https://github.com/transcranial/keras-js license: MIT writeup: "" authors: [Leon Chen] tags: [convnet, rnn] uses: [demo, library] img: https://transcranial.github.io/keras-js/demos/assets/resnet50.png - name: "Interactive visualization of word analogies in GloVe" desc: "king - man + woman is queen; but why? Explore word vectors interactively." url: https://lamyiowce.github.io/word2viz/ repo: https://github.com/lamyiowce/word2viz license: MIT writeup: http://p.migdal.pl/2017/01/06/king-man-woman-queen-why.html authors: [Julia Bazińska, Piotr Migdał] tags: [nlp, word2vec] uses: [demo] img: https://lamyiowce.github.io/word2viz/word2viz_screenshot.png - name: "A Neural Network Playground" desc: "Interactive visualization of simple neural networks, written in typescript using d3.js." url: http://playground.tensorflow.org/ repo: https://github.com/tensorflow/playground license: Apache writeup: "" authors: [Daniel Smilkov, Shan Carter] tags: [mlp] uses: [demo] img: https://playground.tensorflow.org/preview.png - name: "A visual introduction to machine learning (Part 1)" url: http://www.r2d3.us/visual-intro-to-machine-learning-part-1/ desc: "A demo of using a decision tree algorithm to distinguish houses in New York from houses in San Francisco" repo: "" license: "" writeup: "" authors: [Stephanie Yee, Tony Chu] tags: [decision-tree] uses: [demo, tutorial] img: https://www.r2d3.us/static/pages/decision-trees-part-1/preview-en.png - name: "A visual introduction to machine learning (Part 2)" desc: Model Tuning and the Bias-Variance Tradeoff url: http://www.r2d3.us/visual-intro-to-machine-learning-part-2/ repo: "" license: "" writeup: "" authors: [Stephanie Yee, Tony Chu] tags: [decision-tree, overfitting] uses: [demo, tutorial] img: https://www.r2d3.us/static/pages/decision-trees-part2-v2/part2-preview-en.png - name: "D3 Graph Theory" desc: "Learn graph theory using interactive visualizations made in D3.js." url: https://mrpandey.github.io/d3graphTheory/index.html repo: https://github.com/mrpandey/d3graphTheory license: GPL writeup: "" authors: [Avinash Pandey] tags: [graph] uses: [demo, tutorial] img: https://i.imgur.com/MAvlQgj.png - name: "Immersive linear algebra" desc: "The world's first linear algebra book with fully interactive figures." url: http://immersivemath.com/ila/index.html repo: "" license: "" writeup: "" authors: [J. Ström, K. Åström, and T. Akenine-Mölle] tags: [math, linear-algebra] uses: [book] img: imgs/immersivemath.png - name: "Seeing Theory" desc: "A visual introduction to probability and statistics" url: http://students.brown.edu/seeing-theory/ repo: https://github.com/seeingtheory/Seeing-Theory license: Apache writeup: "" authors: [Daniel Kunin, Jingru Guo, Tyler Dae Devlin, Daniel Xiang] tags: [statistics] uses: [tutorial] img: https://students.brown.edu/seeing-theory/img/share/home.png - name: "tSNE for the Web" desc: "TensorFlow.js Powered tSNE Implementation" url: https://nicola17.github.io/tfjs-tsne-demo/ repo: https://github.com/tensorflow/tfjs-tsne license: Apache writeup: https://ai.googleblog.com/2018/06/realtime-tsne-visualizations-with.html authors: [Nicola Pezzotti, Alexander Mordvintsev, Thomas Hollt, Boudewijn P. F. Lelieveldt, Elmar Eisemann, Anna Vilanova] tags: [tsne] uses: [demo, tensorflow.js, npm] img: https://3.bp.blogspot.com/-NE01azL_JxU/Wxli17oYNzI/AAAAAAAACxQ/axOI2yy-Ft0QbqaekOyemm5Xn0wAFvRUwCLcBGAs/s1600/image2.gif - name: "GRAIL Text Recognizer" desc: "An active essay revisiting Gabriel Groner's GRAIL handwriting recognizer from the 1960s" url: https://jackschaedler.github.io/handwriting-recognition/ repo: https://github.com/jackschaedler/handwriting-recognition license: MIT writeup: "" authors: [Jack Schaedler] tags: [handwriting] uses: [demo, tutorial] img: imgs/handwriting-recognition.png - name: "pix2pix: Image-to-Image Demo" desc: "Tensorflow port of Image-to-Image Translation with Conditional Adversarial Nets" url: https://affinelayer.com/pixsrv/ repo: https://github.com/affinelayer/pix2pix-tensorflow license: MIT writeup: "" authors: [Christopher Hesse] tags: [gan, deep-learning] uses: [demo, backend-dependent] img: https://raw.githubusercontent.com/affinelayer/pix2pix-tensorflow/master/docs/examples.jpg - name: "nlp-compromise" desc: "Modest natural-language processing in JavaScript" url: http://compromise.cool/ repo: https://github.com/spencermountain/compromise license: MIT writeup: "" authors: [Spencer Kelly] tags: [nlp] uses: [demo, library] img: imgs/compromise.png - name: "NeuroJS" desc: "A JavaScript deep learning and reinforcement learning library" url: https://janhuenermann.com/projects/learning-to-drive repo: https://github.com/janhuenermann/neurojs license: MIT writeup: "" authors: [Jan Hünermann] tags: [deep-learning, reinforcement-learning] uses: [demo, library] img: https://raw.githubusercontent.com/janhuenermann/neurojs/experimental/images/logo-with-demo.png - name: "DeepForge" desc: "A modern development environment for deep learning" url: http://deepforge.org/ repo: https://github.com/deepforge-dev/deepforge license: Apache writeup: "" authors: [Brian Broll] tags: [deep-learning] uses: [demo, library] img: https://raw.githubusercontent.com/deepforge-dev/deepforge/master/images/overview.png - name: "synaptic" desc: "The javascript architecture-free neural network library for node.js and the browser" url: http://caza.la/synaptic/ repo: https://github.com/cazala/synaptic license: MIT writeup: "" authors: [Brian Broll] tags: [deep-learning] uses: [demo, library] img: imgs/synaptic.png - name: "Machine learning library for Node.js" desc: "Logistic regression, SVM linear and radial, kNN" url: http://joonku.com/project/machine_learning repo: https://github.com/junku901/machine_learning license: MIT writeup: "" authors: [Joon-Ku Kang] tags: [machine-learning] uses: [demo, library, npm] img: "" - name: "Neural Slime Volleyball" desc: "HTML5-JS Slime Volleyball clone created using ConvNetJS." url: http://otoro.net/slimevolley/ repo: https://github.com/hardmaru/neuralslimevolley license: GPL writeup: http://blog.otoro.net/2015/03/28/neural-slime-volleyball/ authors: [hardmaru] tags: [deep-learning] uses: [demo] img: imgs/slimevolley.png - name: "Quick, draw!" desc: "Can a neural network learn to recognize your doodling? The game uses the world’s largest doodling dataset." url: https://quickdraw.withgoogle.com/ repo: "" license: "" writeup: https://ai.googleblog.com/2017/04/teaching-machines-to-draw.html authors: [hardmaru] tags: [deep-learning] uses: [game, backend-dependent] img: https://1.bp.blogspot.com/-X0o-v6SQXtI/WO6R8X22wlI/AAAAAAAABts/Qbvu5QoVwZIfKB-zEV7Po_Juh9AqEb28gCLcB/s1600/twitter_cover.png - name: "SegNet" desc: "A Deep Convolutional Encoder-Decoder Architecture for Robust Semantic Pixel-Wise Labelling" url: https://mi.eng.cam.ac.uk/projects/segnet/#demo repo: https://github.com/alexgkendall/caffe-segnet license: CC BY NC writeup: https://mi.eng.cam.ac.uk/projects/segnet/#research authors: [Alex Kendall, Vijay Badrinarayanan, Roberto Cipolla] tags: [deep-learning, cnn, segmentation] uses: [demo, backend-dependent] img: imgs/segnet.png - name: "Caffe Demos" desc: "Web image classification demo" url: http://demo.caffe.berkeleyvision.org/ repo: "" license: BSD 2-Clause license writeup: http://caffe.berkeleyvision.org/ authors: [Yangqing Jia] tags: [deep-learning, cnn, classification, image-classification] uses: [demo, backend-dependent] img: imgs/caffe-demos.png - name: "LSTM Music Maker" desc: "How evlolved LSTMs improvise on a melody you specify?" url: https://www.sentient.ai/sentient-labs/ea/lstm-music/ repo: "" license: "" writeup: https://arxiv.org/abs/1803.04439 authors: [Aditya Rawal, Risto Miikkulainen] tags: [deep-learning, lstm, music] uses: [demo] img: imgs/lstm-music-maker.png - name: "OMNI Draw" desc: "How do the learned models perceive the characters you draw?" url: https://www.sentient.ai/sentient-labs/ea/omni-draw/ repo: "" license: "" writeup: https://arxiv.org/abs/1803.03745 authors: [Jason Liang, Elliot Meyerson, Risto Miikkulainen] tags: [deep-learning, cmtr, omniglot] uses: [demo] img: imgs/omni-draw.png - name: "Celeb Match" desc: "Which celebrity faces match best the attribute values you specify?" url: https://www.sentient.ai/sentient-labs/ea/celeb-match/ repo: "" license: "" writeup: https://arxiv.org/abs/1803.04062 authors: [Elliot Meyerson, Risto Miikkulainen] tags: [deep-learning, pta] uses: [demo] img: imgs/celeb-match.png - name: "Neural Titanic" desc: "This visualization uses TensorFlow.js to train a neural network on the titanic dataset" url: https://andrewnetwork.github.io/NeuralTitanic/dist/ repo: https://github.com/Andrewnetwork/NeuralTitanic license: MIT writeup: http://kexp.io/intro_tensorflowjs/ authors: [Andrew Ribeiro] tags: [mlp, titanic] uses: [demo, tensorflowjs] img: imgs/neural-titanic.png - name: "Backpropagation algorithm" desc: "The backpropagation algorithm is essential for training large neural networks quickly. This article explains how the algorithm works." url: https://google-developers.appspot.com/machine-learning/crash-course/backprop-scroll/ repo: "" license: "" writeup: "" authors: [Daniel Smilkov] tags: [neural-networks, math] uses: [tutorial] img: imgs/backpropagation-algorithm.png - name: "Visual Search" desc: "This website uses deep neural networks to compute a fingerprint of a query image and retrieves images with similar visual features." url: https://thomasdelteil.github.io/VisualSearch_MXNet/ repo: "https://github.com/ThomasDelteil/VisualSearch_MXNet" license: "" writeup: "" authors: [Thomas Delteil] tags: [neural-networks, deep-learning, search, mxnet] uses: [tutorial, demo, backend-dependent] img: imgs/visual-search.png - name: bayes.js desc: "An implementation of a small MCMC framework and some likelihood functions for doing Bayes stats in the browser." url: https://github.com/rasmusab/bayes.js repo: https://github.com/rasmusab/bayes.js license: MIT writeup: "" authors: [Rasmus Bååth] tags: [bayesian, mcmc, statistics] uses: [demo, library] img: https://raw.githubusercontent.com/rasmusab/bayes.js/master/media/normal_model_plotly.png - name: Basic Neural Network Math desc: "A Visual And Interactive Look at Basic Neural Network Math" url: https://jalammar.github.io/feedforward-neural-networks-visual-interactive/ repo: https://github.com/jalammar/jalammar.github.io/blob/master/_posts/2018-03-23-feedforward-neural-networks-visual-interactive.md license: "" writeup: "" authors: [Jay Alammar] tags: [neural-networks] uses: [tutorial] img: https://jalammar.github.io/images/two-input-one-output-sigmoid-network.png - name: Seedbank desc: "Collection of Interactive Machine Learning Examples" url: http://tools.google.com/seedbank/ repo: "" license: "" writeup: https://medium.com/tensorflow/seedbank-discover-machine-learning-examples-2ff894542b57 authors: [Mike Tyka, Sures Kumar Thoddu Srinivasan, Chris Boudreaux, Simon Doury, Harini Krishnamurthy, Mike Dory, Gabriel Schubiner, Kyle Pedersen, Artists & Machine Intelligence, Colaboratory teams] tags: [neural-networks, deep-learning] uses: [collection, backend-dependent] img: https://tools.google.com/seedbank/static/images/quick-start.gif - name: "The Beginner's Guide to Dimensionality Reduction" desc: "Explore the methods that data scientists use to visualize high-dimensional data." url: https://idyll.pub/post/dimensionality-reduction-293e465c2a3443e8941b016d/ repo: https://github.com/mathisonian/dimensionality-reduction license: MIT writeup: "" authors: [Matthew Conlen, Fred Hohman] tags: [pca, tsne] uses: [tutorial] img: https://idyll.pub/post/dimensionality-reduction-293e465c2a3443e8941b016d/static/images/share.png - name: "Visualizing K-Means Clustering" desc: "The k-means algorithm captures the insight that each point in a cluster should be near to the center of that cluster." url: https://www.naftaliharris.com/blog/visualizing-k-means-clustering/ repo: "" license: "" writeup: "" authors: [Naftali Harris] tags: [kmeans] uses: [tutorial] img: imgs/visualizing-k-means-clustering.png date: "2014-01-19" - name: "Matrix factorization visualized" desc: "Matrix factorization implemented in pure JavaScript. Also for sparse data and logistic regression." url: https://p.migdal.pl/matrix-decomposition-viz/ repo: https://github.com/stared/matrix-decomposition-viz license: MIT writeup: "" authors: [Piotr Migdał] tags: [matrix-factorization] uses: [demo] img: imgs/matrix-decomposition-viz.png date: "2017-10-17" - name: "AI Experiments - Experiments with Google" desc: "AI Experiments is a showcase for simple experiments that make it easier for anyone to start exploring machine learning, through pictures, drawings, language, music, and more." url: https://experiments.withgoogle.com/collection/ai repo: "" license: "" writeup: "" authors: [Google] tags: [matrix-factorization] uses: [collection] img: imgs/experiments-with-google.png - name: "A New Angle on L2 Regularization" desc: "An explorable explanation on the phenomenon of adversarial examples in linear classification and its relation to L2 regularization." url: https://thomas-tanay.github.io/post--L2-regularization/ repo: https://github.com/thomas-tanay/post--L2-regularization license: "" writeup: "" authors: [Thomas Tanay, Lewis D Griffin] tags: [svm, overfitting, adversarial-examples] uses: [tutorial, paper] img: "imgs/a-new-angle-on-l2-reg.png" date: "2018-06-21" - name: "Support Vector Machine Explorer" desc: "This is a demo of the Dash interactive Python framework developed by Plotly." url: https://dash-svm.plot.ly/ repo: https://github.com/plotly/dash-svm license: MIT writeup: "" authors: [Xing Han Lu] tags: [svm] uses: [demo, backend-dependent, plotly, scikit-learn] img: imgs/dash-svm-plot-ly.png date: "2018-08-09" - name: "GAN lab" desc: "Play with Generative Adversarial Networks (GANs) in your browser!" url: https://poloclub.github.io/ganlab/ repo: https://github.com/poloclub/ganlab license: Apache writeup: "" authors: [Minsuk Kahng, Nikhil Thorat, Polo Chau, Fernanda Viégas, and Martin Wattenberg] tags: [GAN, deep-learning, tensorflowjs] uses: [demo] img: imgs/ganlab.png date: "2018-09-04" - name: "Semantic Calculator" desc: "Do math with word embeddings!" url: https://semantic.a9.io/ repo: "" license: "" writeup: "" authors: [Max Krieger] tags: [word2vec, glove, word-embeddings] uses: [demo, backend-dependent] img: imgs/semantic-calculator.png date: "2018-09-29" - name: "ml5.js" desc: "ml5.js aims to make machine learning approachable for a broad audience of artists, creative coders, and students. The library provides access to machine learning algorithms and models in the browser, building on top of TensorFlow.js with no other external dependencies." url: https://ml5js.org/ repo: https://github.com/ml5js/ml5-library license: MIT writeup: https://towardsdatascience.com/introduction-to-ml5-js-3fe51d6a4661 authors: [NYU ITP teachers, residents and students] tags: [machine-learning, tensorflowjs] uses: [tutorial, demo, library] img: imgs/ml5.png - name: "Drum Patterns from Latent Space" desc: "A latent explorable space with some recognizable genre areas." url: http://altsoph.com/pp/dsp/map.html# repo: https://github.com/altsoph/drum_space license: MIT writeup: https://towardsdatascience.com/drum-patterns-from-latent-space-23d59dd9d827 authors: [Aleksey Tikhonov] tags: [music, latent] uses: [demo] img: imgs/drum_space.png date: "2019-03-10"