[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [stared]\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n.config"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Piotr Migdał\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# interactive-machine-learning-list\n\nA collaborative list of interactive Machine Learning, Deep Learning and Statistics websites.\nStarted by [Piotr Migdał](https://p.migdal.pl/), but anyone is encouraged to contribute!\nIt is a simple no-build Vue.js website:\n\n* [p.migdal.pl/interactive-machine-learning-list/](https://p.migdal.pl/interactive-machine-learning-list/)\n\nFeel invited to Pull Request other interactive visualizations (check [websites.yaml](https://github.com/stared/interactive-machine-learning-list/blob/master/websites.yaml))! :)\n\n...aaand if you want to create such visualizations by yourself, see [In Browser AI](https://inbrowser.ai/).\n\n## What goes there?\n\nStill I am thinking what is the best criterion.\n\nFor sure things that are front-end (i.e. JavaScript within browser).\nFor 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:\n\n* make sure it has some didactic value (otherwise ALL services using ML would qualify)\n* add `backend-dependent` in `uses`\n\nStrong 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).\n\n## Other lists\n\n* [Explorable Explanations](http://explorabl.es/)\n* [Distill](https://distill.pub/)\n* [Explained Visually](http://setosa.io/ev/)\n* [AI Experiments with Google](https://experiments.withgoogle.com/collection/ai)\n\n## Inspirations\n\nRead [Explorable Explanations](http://worrydream.com/ExplorableExplanations/) by Bret Victor.\n\nInspirations for collecting and displaying content:\n\n* [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)\n* [Kaggle Past Solutions](http://ndres.me/kaggle-past-solutions/) - a searchable compilation of Kaggle past solutions\n  * source: [EliotAndres/kaggle-past-solutions](https://github.com/EliotAndres/kaggle-past-solutions)\n* [D3 Discovery](https://d3-discovery.net/) - finding D3 plugins with ease\n  * source: [https://github.com/wbkd/d3-discovery](https://github.com/wbkd/d3-discovery)\n\n\n## Design\n\nMain layout and styling developed by [Jakub Fogel](https://github.com/fogelkuba)\n\n## TO DO\n\n(You are invited to constribute)\n\n* Descriptions of sites\n* Write-up in a different way\n* Some sorting (alphabetical?)\n* Share button\n* Code refactoring :)\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>Interactive Machine Learning List</title>\n  <meta name=\"description\" content=\"A collaborative list of interactive Machine Learning, Deep Learning and Statistics websites.\">\n  <meta name=\"keywords\" content=\"museum, scavenger hunt, object detection, ai\">\n  <meta name=\"author\" content=\"Piotr Migdał, Jakub Fogel, contributors\">\n  <meta property=\"og:image\" content=\"https://p.migdal.pl/interactive-machine-learning-list/screenshot.png\" >\n  <meta charset=\"utf-8\">\n  <script src=\"https://www.unpkg.com/vue@2.7.14/dist/vue.js\"></script>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.12.0/js-yaml.min.js\"></script>\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.blue-pink.min.css\"/>\n  <script defer src=\"https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.min.js\"></script>\n  <link rel=\"stylesheet\" href=\"src/style.css\">\n  <script src=\"src/particles.js\"></script>\n  <meta name=\"viewport\"\n  content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n</head>\n<body>\n  <div id=\"app\">\n    <!-- Always shows a header, even in smaller screens. -->\n\n    <div class=\"mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n      <header class=\"mdl-layout__header\">\n        <div class=\"mdl-layout__header-row\">\n          <!-- Title -->\n          <span class=\"mdl-layout-title\">Interactive Machine Learning, Deep Learning and Statistics websites</span>\n          <!-- Add spacer, to align navigation to the right -->\n          <div class=\"mdl-layout-spacer\"></div>\n          <!-- Navigation. We hide it in small screens. -->\n          <nav class=\"mdl-navigation mdl-layout--large-screen-only\">\n            <a class=\"mdl-navigation__link\" href=\"https://github.com/stared/interactive-machine-learning-list/\">GitHub repo</a>\n          </nav>\n        </div>\n      </header>\n      <main class=\"mdl-layout__content\" id=\"particles-js\">\n        <div class=\"page-content\"><!-- Your content goes here -->\n          <div class=\"mdl-grid\">\n            <div class=\"mdl-cell--5-col mdl-cell\" style=\"text-align: right\">\n              <div class=\"illustration\"></div>\n            </div>\n            <div class=\"mdl-cell--6-col mdl-cell\" style=\"padding: 0 2rem\">\n              <h4>Open source: see its <a href=\"https://github.com/stared/interactive-machine-learning-list/\">GitHub repository</a></h4>\n              <h4>It's collaborative: add visualizations via <a href=\"https://github.com/stared/interactive-machine-learning-list/pulls\">pull requests</a></h4>\n              <h4>If you like it,\n              <a class=\"twitter-share-button\" href=\"https://twitter.com/intent/tweet?text=A%20collaborative%20list%20of%20interactive%20Machine%20Learning,%20Deep%20Learning%20and%20Statistics%20websites%20@InBrowserAI\" data-size=\"large\">share it on Twitter</a>, if you want to create such visualizations, sign up for <a href=\"https://inbrowser.ai/\">In Browser AI</a>!</h4>\n              <template id=\"tag-selector\">\n                <div class=\"tags-wrapper\">\n                  <a v-for=\"tag in allTags\"\n                  class=\"tag\"\n                  @click=\"addRemoveToFilters(tag)\"\n                  v-bind:class=\"{ active: checkActive(tag) }\"\n                  v-if=\"getTagCount(tag) > 0\"\n                  >\n                  {{tag}}\n                  <small class=\"tag-count\">{{getTagCount(tag)}}</small>\n                </a>\n                <p v-if=\"filters.length\">\n                  Selected {{filters.length}} filters\n                  <a class=\"tag\"\n                  v-if=\"filters.length\"\n                  style=\"background: rgb(255,64,129); color: white\"\n                  @click=\"clearAllFilters\"\n                  >\n                  <i class=\"material-icons delete\">delete</i>\n                  Clear\n                </a>\n              </p>\n            </div>\n          </template>\n        </div>\n      </div>\n\n\n      <ul class=\"mdl-grid\" style=\"background: rgba(254,254,254, 1);\">\n        <li class=\"mdl-cell mdl-cell--4-col\"\n        v-for=\"(website, id) in filteredWebsites\"\n        >\n        <div class=\"demo-card-wide mdl-card mdl-shadow--2dp\">\n          <div class=\"background\" :style=\"website.img ? {backgroundImage: 'url(' + website.img + ')'} : {}\">\n            <div class=\"mdl-card__title\">\n              <h3 class=\"mdl-card__title-text\">\n                <a :href=\"website.url\" style=\"color: white\">{{ website.name }}</a>\n              </h3>\n            </div>\n            <div class=\"mdl-card__supporting-text\">\n              <span class=\"authors\">\n                by {{ etAl(website.authors) }}\n              </span>\n              <div class=\"mdl-grid\" v-if=\"website.tags && website.uses\">\n                <div>\n                  <i class=\"material-icons\" :id=\"id + 'tagIcon'\">label</i>\n                  <div class=\"mdl-tooltip\" :data-mdl-for=\"id + 'tagIcon'\">\n                    Tags\n                  </div>\n                  <span v-for=\"tag in website.tags\">\n                    <a class=\"tag\" @click=\"addRemoveToFilters(tag)\" v-bind:class=\"{ active: checkActive(tag) }\">{{ tag }}</a>\n                  </span>\n                </div>\n                <div>\n                  <i class=\"material-icons\" :id=\"id + 'tagUses'\">timeline</i>\n                  <div class=\"mdl-tooltip\" :data-mdl-for=\"id + 'tagUses'\">\n                    Uses\n                  </div>\n                  <span v-for=\"use in website.uses\">\n                    <a class=\"tag\" @click=\"addRemoveToFilters(use)\" v-bind:class=\"{ active: checkActive(use) }\">{{ use }}</a>\n                  </span>\n                </div>\n                <div>\n                  <i class=\"material-icons\" :id=\"id + 'tagLicense'\">copyright</i>\n                  <div class=\"mdl-tooltip\" :data-mdl-for=\"id + 'tagLicense'\">\n                    License\n                  </div>\n                  <span v-if=\"website.license\">\n                    <a class=\"tag\" @click=\"addRemoveToFilters(website.license)\" v-bind:class=\"{ active: checkActive(website.license) }\">{{ website.license }}</a>\n                  </span>\n                </div>\n              </div>\n            </div>\n\n            <div class=\"mdl-card__menu\">\n              <a :href=\"website.repo\" :id=\"id + 'repo'\" v-if=\"website.repo\">\n                <button class=\"mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect\">\n                  <i class=\"material-icons\">code</i>\n                </button>\n              </a>\n              <div class=\"mdl-tooltip\" :data-mdl-for=\"id + 'repo'\">\n                Repository\n              </div>\n\n              <a :href=\"website.url\" :id=\"id + 'www'\" v-if=\"website.url\">\n                <button class=\"mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect\">\n                  <i class=\"material-icons\">language</i>\n                </button>\n              </a>\n              <div class=\"mdl-tooltip\" :data-mdl-for=\"id + 'www'\">\n                Website\n              </div>\n            </div>\n          </div>\n\n          <div class=\"mdl-card__actions mdl-card--border mdl-card--bottom\"\n          v-if=\"website.writeup || website.desc\">\n          <span v-if=\"website.desc\"> {{ website.desc }}</span><br>\n          <a :href=\"website.writeup\"\n          v-if=\"website.writeup\">\n          see also this article\n        </a>\n      </div>\n    </div>\n  </li>\n</ul>\n\n<footer class=\"mdl-mini-footer\">\n  <div class=\"mdl-mini-footer__left-section\">\n    <ul class=\"mdl-mini-footer__link-list\">\n      <li><a href=\"https://github.com/stared/interactive-machine-learning-list\">open source (contribute!)</a></li>\n      <li>idea by <a href=\"https://p.migdal.pl\">Piotr Migdał</a></li>\n      <li>front-end by <a href=\"https://github.com/fogelkuba\">Jakub Fogel</a></li>\n      <li>brain graphics by <a href='https://pl.freepik.com/darmowe-wektory/mozg-mechaniczna_769574.htm'>Freepik\n      </a></li>\n      <li>contributions by <a href=\"https://github.com/stared/interactive-machine-learning-list/graphs/contributors\">many</a></li>\n    </ul>\n    <p>...aaand if you want to create such visualizations by yourself, see <a href=\"https://inbrowser.ai/\">In Browser AI</a></p>\n  </div>\n</footer>\n</div>\n</main>\n</div>\n\n\n<!--app end-->\n</div>\n<script src=\"src/main.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "src/main.js",
    "content": "document.addEventListener('DOMContentLoaded', function () {\n  /* particlesJS.load(@dom-id, @path-json, @callback (optional)); */\n  particlesJS.load('particles-js', 'src/particlesjs-config.json', function () {\n    console.log('callback - particles.js config loaded');\n  });\n});\n\n\nVue.component('tag-selector', {\n  props: ['all-tags'],\n  template: document.getElementById('tag-selector'),\n});\n\n\nconst app = new Vue({\n  el: '#app',\n  data: {\n    websites: [],\n    allTags: [],\n    filters: []\n  },\n  computed: {\n    filteredWebsites: function() {\n      return this.websites.filter((website) =>\n        this.filters.every((tag) =>\n          website.allTags.includes(tag)\n        )\n      );\n\n    }\n  },\n  created: function() {\n    const that = this;\n    fetch(\"websites.yaml\")\n    .then(response => response.text())\n    .then(text => {\n      that.websites = jsyaml.load(text);\n      that.websites.forEach((website) => {\n        website.allTags = website.tags.concat(website.uses);\n        if (!!website.license) {\n          website.allTags.push(website.license);\n        }\n      });\n      that.websites.forEach((website) => {\n        website.allTags.forEach((tag) => {\n          if (that.allTags.indexOf(tag) === -1) {\n            that.allTags.push(tag);\n          }\n        });\n      });\n      that.allTags.sort((a, b) =>\n        a.toLowerCase() < b.toLowerCase() ? -1 : 1\n      );\n    });\n  },\n  methods: {\n    etAl: (authors) => {\n      if (authors.length < 3) {\n        return authors.join(\", \");\n      } else {\n        return authors.slice(0, 3).join(\", \") + \" et al.\";\n      }\n    },\n    addRemoveToFilters: function(tag) {\n      if (this.filters.indexOf(tag) === -1) {\n        this.filters.push(tag);\n      } else {\n        this.filters = this.filters\n        .filter((x) => x !== tag);\n      }\n    },\n    checkActive: function(tag) {\n      return this.filters.indexOf(tag) > -1;\n    },\n    clearAllFilters: function() {\n      this.filters = [];\n    },\n    getTagCount: function(tag) {\n      return this.filteredWebsites\n        .filter((website) => website.allTags.includes(tag))\n        .length;\n\n    }\n  }\n});\n"
  },
  {
    "path": "src/particles.js",
    "content": "/* -----------------------------------------------\n/* Author : Vincent Garreau  - vincentgarreau.com\n/* MIT license: http://opensource.org/licenses/MIT\n/* Demo / Generator : vincentgarreau.com/particles.js\n/* GitHub : github.com/VincentGarreau/particles.js\n/* How to use? : Check the GitHub README\n/* v2.0.0\n/* ----------------------------------------------- */\n\nvar pJS = function(tag_id, params){\n\n    var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el');\n\n    /* particles.js variables with default values */\n    this.pJS = {\n        canvas: {\n            el: canvas_el,\n            w: canvas_el.offsetWidth,\n            h: canvas_el.offsetHeight\n        },\n        particles: {\n            number: {\n                value: 400,\n                density: {\n                    enable: true,\n                    value_area: 800\n                }\n            },\n            color: {\n                value: '#fff'\n            },\n            shape: {\n                type: 'circle',\n                stroke: {\n                    width: 0,\n                    color: '#ff0000'\n                },\n                polygon: {\n                    nb_sides: 5\n                },\n                image: {\n                    src: '',\n                    width: 100,\n                    height: 100\n                }\n            },\n            opacity: {\n                value: 1,\n                random: false,\n                anim: {\n                    enable: false,\n                    speed: 2,\n                    opacity_min: 0,\n                    sync: false\n                }\n            },\n            size: {\n                value: 20,\n                random: false,\n                anim: {\n                    enable: false,\n                    speed: 20,\n                    size_min: 0,\n                    sync: false\n                }\n            },\n            line_linked: {\n                enable: true,\n                distance: 100,\n                color: '#fff',\n                opacity: 1,\n                width: 1\n            },\n            move: {\n                enable: true,\n                speed: 2,\n                direction: 'none',\n                random: false,\n                straight: false,\n                out_mode: 'out',\n                bounce: false,\n                attract: {\n                    enable: false,\n                    rotateX: 3000,\n                    rotateY: 3000\n                }\n            },\n            array: []\n        },\n        interactivity: {\n            detect_on: 'canvas',\n            events: {\n                onhover: {\n                    enable: true,\n                    mode: 'grab'\n                },\n                onclick: {\n                    enable: true,\n                    mode: 'push'\n                },\n                resize: true\n            },\n            modes: {\n                grab:{\n                    distance: 100,\n                    line_linked:{\n                        opacity: 1\n                    }\n                },\n                bubble:{\n                    distance: 200,\n                    size: 80,\n                    duration: 0.4\n                },\n                repulse:{\n                    distance: 200,\n                    duration: 0.4\n                },\n                push:{\n                    particles_nb: 4\n                },\n                remove:{\n                    particles_nb: 2\n                }\n            },\n            mouse:{}\n        },\n        retina_detect: false,\n        fn: {\n            interact: {},\n            modes: {},\n            vendors:{}\n        },\n        tmp: {}\n    };\n\n    var pJS = this.pJS;\n\n    /* params settings */\n    if(params){\n        Object.deepExtend(pJS, params);\n    }\n\n    pJS.tmp.obj = {\n        size_value: pJS.particles.size.value,\n        size_anim_speed: pJS.particles.size.anim.speed,\n        move_speed: pJS.particles.move.speed,\n        line_linked_distance: pJS.particles.line_linked.distance,\n        line_linked_width: pJS.particles.line_linked.width,\n        mode_grab_distance: pJS.interactivity.modes.grab.distance,\n        mode_bubble_distance: pJS.interactivity.modes.bubble.distance,\n        mode_bubble_size: pJS.interactivity.modes.bubble.size,\n        mode_repulse_distance: pJS.interactivity.modes.repulse.distance\n    };\n\n\n    pJS.fn.retinaInit = function(){\n\n        if(pJS.retina_detect && window.devicePixelRatio > 1){\n            pJS.canvas.pxratio = window.devicePixelRatio;\n            pJS.tmp.retina = true;\n        }\n        else{\n            pJS.canvas.pxratio = 1;\n            pJS.tmp.retina = false;\n        }\n\n        pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio;\n        pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio;\n\n        pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio;\n        pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio;\n        pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio;\n        pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio;\n        pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio;\n        pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio;\n        pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio;\n        pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio;\n        pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio;\n\n    };\n\n\n\n    /* ---------- pJS functions - canvas ------------ */\n\n    pJS.fn.canvasInit = function(){\n        pJS.canvas.ctx = pJS.canvas.el.getContext('2d');\n    };\n\n    pJS.fn.canvasSize = function(){\n\n        pJS.canvas.el.width = pJS.canvas.w;\n        pJS.canvas.el.height = pJS.canvas.h;\n\n        if(pJS && pJS.interactivity.events.resize){\n\n            window.addEventListener('resize', function(){\n\n                pJS.canvas.w = pJS.canvas.el.offsetWidth;\n                pJS.canvas.h = pJS.canvas.el.offsetHeight;\n\n                /* resize canvas */\n                if(pJS.tmp.retina){\n                    pJS.canvas.w *= pJS.canvas.pxratio;\n                    pJS.canvas.h *= pJS.canvas.pxratio;\n                }\n\n                pJS.canvas.el.width = pJS.canvas.w;\n                pJS.canvas.el.height = pJS.canvas.h;\n\n                /* repaint canvas on anim disabled */\n                if(!pJS.particles.move.enable){\n                    pJS.fn.particlesEmpty();\n                    pJS.fn.particlesCreate();\n                    pJS.fn.particlesDraw();\n                    pJS.fn.vendors.densityAutoParticles();\n                }\n\n                /* density particles enabled */\n                pJS.fn.vendors.densityAutoParticles();\n\n            });\n\n        }\n\n    };\n\n\n    pJS.fn.canvasPaint = function(){\n        pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n    };\n\n    pJS.fn.canvasClear = function(){\n        pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n    };\n\n\n    /* --------- pJS functions - particles ----------- */\n\n    pJS.fn.particle = function(color, opacity, position){\n\n        /* size */\n        this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value;\n        if(pJS.particles.size.anim.enable){\n            this.size_status = false;\n            this.vs = pJS.particles.size.anim.speed / 100;\n            if(!pJS.particles.size.anim.sync){\n                this.vs = this.vs * Math.random();\n            }\n        }\n\n        /* position */\n        this.x = position ? position.x : Math.random() * pJS.canvas.w;\n        this.y = position ? position.y : Math.random() * pJS.canvas.h;\n\n        /* check position  - into the canvas */\n        if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius;\n        else if(this.x < this.radius*2) this.x = this.x + this.radius;\n        if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius;\n        else if(this.y < this.radius*2) this.y = this.y + this.radius;\n\n        /* check position - avoid overlap */\n        if(pJS.particles.move.bounce){\n            pJS.fn.vendors.checkOverlap(this, position);\n        }\n\n        /* color */\n        this.color = {};\n        if(typeof(color.value) == 'object'){\n\n            if(color.value instanceof Array){\n                var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)];\n                this.color.rgb = hexToRgb(color_selected);\n            }else{\n                if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){\n                    this.color.rgb = {\n                        r: color.value.r,\n                        g: color.value.g,\n                        b: color.value.b\n                    }\n                }\n                if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){\n                    this.color.hsl = {\n                        h: color.value.h,\n                        s: color.value.s,\n                        l: color.value.l\n                    }\n                }\n            }\n\n        }\n        else if(color.value == 'random'){\n            this.color.rgb = {\n                r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),\n                g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),\n                b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0)\n            }\n        }\n        else if(typeof(color.value) == 'string'){\n            this.color = color;\n            this.color.rgb = hexToRgb(this.color.value);\n        }\n\n        /* opacity */\n        this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value;\n        if(pJS.particles.opacity.anim.enable){\n            this.opacity_status = false;\n            this.vo = pJS.particles.opacity.anim.speed / 100;\n            if(!pJS.particles.opacity.anim.sync){\n                this.vo = this.vo * Math.random();\n            }\n        }\n\n        /* animation - velocity for speed */\n        var velbase = {}\n        switch(pJS.particles.move.direction){\n            case 'top':\n                velbase = { x:0, y:-1 };\n                break;\n            case 'top-right':\n                velbase = { x:0.5, y:-0.5 };\n                break;\n            case 'right':\n                velbase = { x:1, y:-0 };\n                break;\n            case 'bottom-right':\n                velbase = { x:0.5, y:0.5 };\n                break;\n            case 'bottom':\n                velbase = { x:0, y:1 };\n                break;\n            case 'bottom-left':\n                velbase = { x:-0.5, y:1 };\n                break;\n            case 'left':\n                velbase = { x:-1, y:0 };\n                break;\n            case 'top-left':\n                velbase = { x:-0.5, y:-0.5 };\n                break;\n            default:\n                velbase = { x:0, y:0 };\n                break;\n        }\n\n        if(pJS.particles.move.straight){\n            this.vx = velbase.x;\n            this.vy = velbase.y;\n            if(pJS.particles.move.random){\n                this.vx = this.vx * (Math.random());\n                this.vy = this.vy * (Math.random());\n            }\n        }else{\n            this.vx = velbase.x + Math.random()-0.5;\n            this.vy = velbase.y + Math.random()-0.5;\n        }\n\n        // var theta = 2.0 * Math.PI * Math.random();\n        // this.vx = Math.cos(theta);\n        // this.vy = Math.sin(theta);\n\n        this.vx_i = this.vx;\n        this.vy_i = this.vy;\n\n\n\n        /* if shape is image */\n\n        var shape_type = pJS.particles.shape.type;\n        if(typeof(shape_type) == 'object'){\n            if(shape_type instanceof Array){\n                var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)];\n                this.shape = shape_selected;\n            }\n        }else{\n            this.shape = shape_type;\n        }\n\n        if(this.shape == 'image'){\n            var sh = pJS.particles.shape;\n            this.img = {\n                src: sh.image.src,\n                ratio: sh.image.width / sh.image.height\n            }\n            if(!this.img.ratio) this.img.ratio = 1;\n            if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){\n                pJS.fn.vendors.createSvgImg(this);\n                if(pJS.tmp.pushing){\n                    this.img.loaded = false;\n                }\n            }\n        }\n\n\n\n    };\n\n\n    pJS.fn.particle.prototype.draw = function() {\n\n        var p = this;\n\n        if(p.radius_bubble != undefined){\n            var radius = p.radius_bubble;\n        }else{\n            var radius = p.radius;\n        }\n\n        if(p.opacity_bubble != undefined){\n            var opacity = p.opacity_bubble;\n        }else{\n            var opacity = p.opacity;\n        }\n\n        if(p.color.rgb){\n            var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')';\n        }else{\n            var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')';\n        }\n\n        pJS.canvas.ctx.fillStyle = color_value;\n        pJS.canvas.ctx.beginPath();\n\n        switch(p.shape){\n\n            case 'circle':\n                pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false);\n                break;\n\n            case 'edge':\n                pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2);\n                break;\n\n            case 'triangle':\n                pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2);\n                break;\n\n            case 'polygon':\n                pJS.fn.vendors.drawShape(\n                    pJS.canvas.ctx,\n                    p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX\n                    p.y - radius / (2.66/3.5), // startY\n                    radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength\n                    pJS.particles.shape.polygon.nb_sides, // sideCountNumerator\n                    1 // sideCountDenominator\n                );\n                break;\n\n            case 'star':\n                pJS.fn.vendors.drawShape(\n                    pJS.canvas.ctx,\n                    p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX\n                    p.y - radius / (2*2.66/3.5), // startY\n                    radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength\n                    pJS.particles.shape.polygon.nb_sides, // sideCountNumerator\n                    2 // sideCountDenominator\n                );\n                break;\n\n            case 'image':\n\n            function draw(){\n                pJS.canvas.ctx.drawImage(\n                    img_obj,\n                    p.x-radius,\n                    p.y-radius,\n                    radius*2,\n                    radius*2 / p.img.ratio\n                );\n            }\n\n                if(pJS.tmp.img_type == 'svg'){\n                    var img_obj = p.img.obj;\n                }else{\n                    var img_obj = pJS.tmp.img_obj;\n                }\n\n                if(img_obj){\n                    draw();\n                }\n\n                break;\n\n        }\n\n        pJS.canvas.ctx.closePath();\n\n        if(pJS.particles.shape.stroke.width > 0){\n            pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color;\n            pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width;\n            pJS.canvas.ctx.stroke();\n        }\n\n        pJS.canvas.ctx.fill();\n\n    };\n\n\n    pJS.fn.particlesCreate = function(){\n        for(var i = 0; i < pJS.particles.number.value; i++) {\n            pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value));\n        }\n    };\n\n    pJS.fn.particlesUpdate = function(){\n\n        for(var i = 0; i < pJS.particles.array.length; i++){\n\n            /* the particle */\n            var p = pJS.particles.array[i];\n\n            // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy;\n            // var f = -BANG_SIZE / d;\n            // if ( d < BANG_SIZE ) {\n            //     var t = Math.atan2( dy, dx );\n            //     p.vx = f * Math.cos(t);\n            //     p.vy = f * Math.sin(t);\n            // }\n\n            /* move the particle */\n            if(pJS.particles.move.enable){\n                var ms = pJS.particles.move.speed/2;\n                p.x += p.vx * ms;\n                p.y += p.vy * ms;\n            }\n\n            /* change opacity status */\n            if(pJS.particles.opacity.anim.enable) {\n                if(p.opacity_status == true) {\n                    if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false;\n                    p.opacity += p.vo;\n                }else {\n                    if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true;\n                    p.opacity -= p.vo;\n                }\n                if(p.opacity < 0) p.opacity = 0;\n            }\n\n            /* change size */\n            if(pJS.particles.size.anim.enable){\n                if(p.size_status == true){\n                    if(p.radius >= pJS.particles.size.value) p.size_status = false;\n                    p.radius += p.vs;\n                }else{\n                    if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true;\n                    p.radius -= p.vs;\n                }\n                if(p.radius < 0) p.radius = 0;\n            }\n\n            /* change particle position if it is out of canvas */\n            if(pJS.particles.move.out_mode == 'bounce'){\n                var new_pos = {\n                    x_left: p.radius,\n                    x_right:  pJS.canvas.w,\n                    y_top: p.radius,\n                    y_bottom: pJS.canvas.h\n                }\n            }else{\n                var new_pos = {\n                    x_left: -p.radius,\n                    x_right: pJS.canvas.w + p.radius,\n                    y_top: -p.radius,\n                    y_bottom: pJS.canvas.h + p.radius\n                }\n            }\n\n            if(p.x - p.radius > pJS.canvas.w){\n                p.x = new_pos.x_left;\n                p.y = Math.random() * pJS.canvas.h;\n            }\n            else if(p.x + p.radius < 0){\n                p.x = new_pos.x_right;\n                p.y = Math.random() * pJS.canvas.h;\n            }\n            if(p.y - p.radius > pJS.canvas.h){\n                p.y = new_pos.y_top;\n                p.x = Math.random() * pJS.canvas.w;\n            }\n            else if(p.y + p.radius < 0){\n                p.y = new_pos.y_bottom;\n                p.x = Math.random() * pJS.canvas.w;\n            }\n\n            /* out of canvas modes */\n            switch(pJS.particles.move.out_mode){\n                case 'bounce':\n                    if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx;\n                    else if (p.x - p.radius < 0) p.vx = -p.vx;\n                    if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy;\n                    else if (p.y - p.radius < 0) p.vy = -p.vy;\n                    break;\n            }\n\n            /* events */\n            if(isInArray('grab', pJS.interactivity.events.onhover.mode)){\n                pJS.fn.modes.grabParticle(p);\n            }\n\n            if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){\n                pJS.fn.modes.bubbleParticle(p);\n            }\n\n            if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){\n                pJS.fn.modes.repulseParticle(p);\n            }\n\n            /* interaction auto between particles */\n            if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){\n                for(var j = i + 1; j < pJS.particles.array.length; j++){\n                    var p2 = pJS.particles.array[j];\n\n                    /* link particles */\n                    if(pJS.particles.line_linked.enable){\n                        pJS.fn.interact.linkParticles(p,p2);\n                    }\n\n                    /* attract particles */\n                    if(pJS.particles.move.attract.enable){\n                        pJS.fn.interact.attractParticles(p,p2);\n                    }\n\n                    /* bounce particles */\n                    if(pJS.particles.move.bounce){\n                        pJS.fn.interact.bounceParticles(p,p2);\n                    }\n\n                }\n            }\n\n\n        }\n\n    };\n\n    pJS.fn.particlesDraw = function(){\n\n        /* clear canvas */\n        pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);\n\n        /* update each particles param */\n        pJS.fn.particlesUpdate();\n\n        /* draw each particle */\n        for(var i = 0; i < pJS.particles.array.length; i++){\n            var p = pJS.particles.array[i];\n            p.draw();\n        }\n\n    };\n\n    pJS.fn.particlesEmpty = function(){\n        pJS.particles.array = [];\n    };\n\n    pJS.fn.particlesRefresh = function(){\n\n        /* init all */\n        cancelRequestAnimFrame(pJS.fn.checkAnimFrame);\n        cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n        pJS.tmp.source_svg = undefined;\n        pJS.tmp.img_obj = undefined;\n        pJS.tmp.count_svg = 0;\n        pJS.fn.particlesEmpty();\n        pJS.fn.canvasClear();\n\n        /* restart */\n        pJS.fn.vendors.start();\n\n    };\n\n\n    /* ---------- pJS functions - particles interaction ------------ */\n\n    pJS.fn.interact.linkParticles = function(p1, p2){\n\n        var dx = p1.x - p2.x,\n            dy = p1.y - p2.y,\n            dist = Math.sqrt(dx*dx + dy*dy);\n\n        /* draw a line between p1 and p2 if the distance between them is under the config distance */\n        if(dist <= pJS.particles.line_linked.distance){\n\n            var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance;\n\n            if(opacity_line > 0){\n\n                /* style */\n                var color_line = pJS.particles.line_linked.color_rgb_line;\n                pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';\n                pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;\n                //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */\n\n                /* path */\n                pJS.canvas.ctx.beginPath();\n                pJS.canvas.ctx.moveTo(p1.x, p1.y);\n                pJS.canvas.ctx.lineTo(p2.x, p2.y);\n                pJS.canvas.ctx.stroke();\n                pJS.canvas.ctx.closePath();\n\n            }\n\n        }\n\n    };\n\n\n    pJS.fn.interact.attractParticles  = function(p1, p2){\n\n        /* condensed particles */\n        var dx = p1.x - p2.x,\n            dy = p1.y - p2.y,\n            dist = Math.sqrt(dx*dx + dy*dy);\n\n        if(dist <= pJS.particles.line_linked.distance){\n\n            var ax = dx/(pJS.particles.move.attract.rotateX*1000),\n                ay = dy/(pJS.particles.move.attract.rotateY*1000);\n\n            p1.vx -= ax;\n            p1.vy -= ay;\n\n            p2.vx += ax;\n            p2.vy += ay;\n\n        }\n\n\n    }\n\n\n    pJS.fn.interact.bounceParticles = function(p1, p2){\n\n        var dx = p1.x - p2.x,\n            dy = p1.y - p2.y,\n            dist = Math.sqrt(dx*dx + dy*dy),\n            dist_p = p1.radius+p2.radius;\n\n        if(dist <= dist_p){\n            p1.vx = -p1.vx;\n            p1.vy = -p1.vy;\n\n            p2.vx = -p2.vx;\n            p2.vy = -p2.vy;\n        }\n\n    }\n\n\n    /* ---------- pJS functions - modes events ------------ */\n\n    pJS.fn.modes.pushParticles = function(nb, pos){\n\n        pJS.tmp.pushing = true;\n\n        for(var i = 0; i < nb; i++){\n            pJS.particles.array.push(\n                new pJS.fn.particle(\n                    pJS.particles.color,\n                    pJS.particles.opacity.value,\n                    {\n                        'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,\n                        'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h\n                    }\n                )\n            )\n            if(i == nb-1){\n                if(!pJS.particles.move.enable){\n                    pJS.fn.particlesDraw();\n                }\n                pJS.tmp.pushing = false;\n            }\n        }\n\n    };\n\n\n    pJS.fn.modes.removeParticles = function(nb){\n\n        pJS.particles.array.splice(0, nb);\n        if(!pJS.particles.move.enable){\n            pJS.fn.particlesDraw();\n        }\n\n    };\n\n\n    pJS.fn.modes.bubbleParticle = function(p){\n\n        /* on hover event */\n        if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){\n\n            var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n                dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n                dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),\n                ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance;\n\n            function init(){\n                p.opacity_bubble = p.opacity;\n                p.radius_bubble = p.radius;\n            }\n\n            /* mousemove - check ratio */\n            if(dist_mouse <= pJS.interactivity.modes.bubble.distance){\n\n                if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){\n\n                    /* size */\n                    if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){\n\n                        if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){\n                            var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio);\n                            if(size >= 0){\n                                p.radius_bubble = size;\n                            }\n                        }else{\n                            var dif = p.radius - pJS.interactivity.modes.bubble.size,\n                                size = p.radius - (dif*ratio);\n                            if(size > 0){\n                                p.radius_bubble = size;\n                            }else{\n                                p.radius_bubble = 0;\n                            }\n                        }\n\n                    }\n\n                    /* opacity */\n                    if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){\n\n                        if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){\n                            var opacity = pJS.interactivity.modes.bubble.opacity*ratio;\n                            if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){\n                                p.opacity_bubble = opacity;\n                            }\n                        }else{\n                            var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio;\n                            if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){\n                                p.opacity_bubble = opacity;\n                            }\n                        }\n\n                    }\n\n                }\n\n            }else{\n                init();\n            }\n\n\n            /* mouseleave */\n            if(pJS.interactivity.status == 'mouseleave'){\n                init();\n            }\n\n        }\n\n        /* on click event */\n        else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){\n\n\n            if(pJS.tmp.bubble_clicking){\n                var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x,\n                    dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y,\n                    dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),\n                    time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000;\n\n                if(time_spent > pJS.interactivity.modes.bubble.duration){\n                    pJS.tmp.bubble_duration_end = true;\n                }\n\n                if(time_spent > pJS.interactivity.modes.bubble.duration*2){\n                    pJS.tmp.bubble_clicking = false;\n                    pJS.tmp.bubble_duration_end = false;\n                }\n            }\n\n\n            function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){\n\n                if(bubble_param != particles_param){\n\n                    if(!pJS.tmp.bubble_duration_end){\n                        if(dist_mouse <= pJS.interactivity.modes.bubble.distance){\n                            if(p_obj_bubble != undefined) var obj = p_obj_bubble;\n                            else var obj = p_obj;\n                            if(obj != bubble_param){\n                                var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration);\n                                if(id == 'size') p.radius_bubble = value;\n                                if(id == 'opacity') p.opacity_bubble = value;\n                            }\n                        }else{\n                            if(id == 'size') p.radius_bubble = undefined;\n                            if(id == 'opacity') p.opacity_bubble = undefined;\n                        }\n                    }else{\n                        if(p_obj_bubble != undefined){\n                            var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration),\n                                dif = bubble_param - value_tmp;\n                            value = bubble_param + dif;\n                            if(id == 'size') p.radius_bubble = value;\n                            if(id == 'opacity') p.opacity_bubble = value;\n                        }\n                    }\n\n                }\n\n            }\n\n            if(pJS.tmp.bubble_clicking){\n                /* size */\n                process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size');\n                /* opacity */\n                process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity');\n            }\n\n        }\n\n    };\n\n\n    pJS.fn.modes.repulseParticle = function(p){\n\n        if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') {\n\n            var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n                dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n                dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);\n\n            var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse},\n                repulseRadius = pJS.interactivity.modes.repulse.distance,\n                velocity = 100,\n                repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50);\n\n            var pos = {\n                x: p.x + normVec.x * repulseFactor,\n                y: p.y + normVec.y * repulseFactor\n            }\n\n            if(pJS.particles.move.out_mode == 'bounce'){\n                if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x;\n                if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y;\n            }else{\n                p.x = pos.x;\n                p.y = pos.y;\n            }\n\n        }\n\n\n        else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) {\n\n            if(!pJS.tmp.repulse_finish){\n                pJS.tmp.repulse_count++;\n                if(pJS.tmp.repulse_count == pJS.particles.array.length){\n                    pJS.tmp.repulse_finish = true;\n                }\n            }\n\n            if(pJS.tmp.repulse_clicking){\n\n                var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3);\n\n                var dx = pJS.interactivity.mouse.click_pos_x - p.x,\n                    dy = pJS.interactivity.mouse.click_pos_y - p.y,\n                    d = dx*dx + dy*dy;\n\n                var force = -repulseRadius / d * 1;\n\n                function process(){\n\n                    var f = Math.atan2(dy,dx);\n                    p.vx = force * Math.cos(f);\n                    p.vy = force * Math.sin(f);\n\n                    if(pJS.particles.move.out_mode == 'bounce'){\n                        var pos = {\n                            x: p.x + p.vx,\n                            y: p.y + p.vy\n                        }\n                        if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx;\n                        else if (pos.x - p.radius < 0) p.vx = -p.vx;\n                        if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy;\n                        else if (pos.y - p.radius < 0) p.vy = -p.vy;\n                    }\n\n                }\n\n                // default\n                if(d <= repulseRadius){\n                    process();\n                }\n\n                // bang - slow motion mode\n                // if(!pJS.tmp.repulse_finish){\n                //   if(d <= repulseRadius){\n                //     process();\n                //   }\n                // }else{\n                //   process();\n                // }\n\n\n            }else{\n\n                if(pJS.tmp.repulse_clicking == false){\n\n                    p.vx = p.vx_i;\n                    p.vy = p.vy_i;\n\n                }\n\n            }\n\n        }\n\n    }\n\n\n    pJS.fn.modes.grabParticle = function(p){\n\n        if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){\n\n            var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,\n                dy_mouse = p.y - pJS.interactivity.mouse.pos_y,\n                dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);\n\n            /* draw a line between the cursor and the particle if the distance between them is under the config distance */\n            if(dist_mouse <= pJS.interactivity.modes.grab.distance){\n\n                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;\n\n                if(opacity_line > 0){\n\n                    /* style */\n                    var color_line = pJS.particles.line_linked.color_rgb_line;\n                    pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';\n                    pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;\n                    //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */\n\n                    /* path */\n                    pJS.canvas.ctx.beginPath();\n                    pJS.canvas.ctx.moveTo(p.x, p.y);\n                    pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y);\n                    pJS.canvas.ctx.stroke();\n                    pJS.canvas.ctx.closePath();\n\n                }\n\n            }\n\n        }\n\n    };\n\n\n\n    /* ---------- pJS functions - vendors ------------ */\n\n    pJS.fn.vendors.eventsListeners = function(){\n\n        /* events target element */\n        if(pJS.interactivity.detect_on == 'window'){\n            pJS.interactivity.el = window;\n        }else{\n            pJS.interactivity.el = pJS.canvas.el;\n        }\n\n\n        /* detect mouse pos - on hover / click event */\n        if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){\n\n            /* el on mousemove */\n            pJS.interactivity.el.addEventListener('mousemove', function(e){\n\n                if(pJS.interactivity.el == window){\n                    var pos_x = e.clientX,\n                        pos_y = e.clientY;\n                }\n                else{\n                    var pos_x = e.offsetX || e.clientX,\n                        pos_y = e.offsetY || e.clientY;\n                }\n\n                pJS.interactivity.mouse.pos_x = pos_x;\n                pJS.interactivity.mouse.pos_y = pos_y;\n\n                if(pJS.tmp.retina){\n                    pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio;\n                    pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio;\n                }\n\n                pJS.interactivity.status = 'mousemove';\n\n            });\n\n            /* el on onmouseleave */\n            pJS.interactivity.el.addEventListener('mouseleave', function(e){\n\n                pJS.interactivity.mouse.pos_x = null;\n                pJS.interactivity.mouse.pos_y = null;\n                pJS.interactivity.status = 'mouseleave';\n\n            });\n\n        }\n\n        /* on click event */\n        if(pJS.interactivity.events.onclick.enable){\n\n            pJS.interactivity.el.addEventListener('click', function(){\n\n                pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x;\n                pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y;\n                pJS.interactivity.mouse.click_time = new Date().getTime();\n\n                if(pJS.interactivity.events.onclick.enable){\n\n                    switch(pJS.interactivity.events.onclick.mode){\n\n                        case 'push':\n                            if(pJS.particles.move.enable){\n                                pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);\n                            }else{\n                                if(pJS.interactivity.modes.push.particles_nb == 1){\n                                    pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);\n                                }\n                                else if(pJS.interactivity.modes.push.particles_nb > 1){\n                                    pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb);\n                                }\n                            }\n                            break;\n\n                        case 'remove':\n                            pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb);\n                            break;\n\n                        case 'bubble':\n                            pJS.tmp.bubble_clicking = true;\n                            break;\n\n                        case 'repulse':\n                            pJS.tmp.repulse_clicking = true;\n                            pJS.tmp.repulse_count = 0;\n                            pJS.tmp.repulse_finish = false;\n                            setTimeout(function(){\n                                pJS.tmp.repulse_clicking = false;\n                            }, pJS.interactivity.modes.repulse.duration*1000)\n                            break;\n\n                    }\n\n                }\n\n            });\n\n        }\n\n\n    };\n\n    pJS.fn.vendors.densityAutoParticles = function(){\n\n        if(pJS.particles.number.density.enable){\n\n            /* calc area */\n            var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000;\n            if(pJS.tmp.retina){\n                area = area/(pJS.canvas.pxratio*2);\n            }\n\n            /* calc number of particles based on density area */\n            var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area;\n\n            /* add or remove X particles */\n            var missing_particles = pJS.particles.array.length - nb_particles;\n            if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles));\n            else pJS.fn.modes.removeParticles(missing_particles);\n\n        }\n\n    };\n\n\n    pJS.fn.vendors.checkOverlap = function(p1, position){\n        for(var i = 0; i < pJS.particles.array.length; i++){\n            var p2 = pJS.particles.array[i];\n\n            var dx = p1.x - p2.x,\n                dy = p1.y - p2.y,\n                dist = Math.sqrt(dx*dx + dy*dy);\n\n            if(dist <= p1.radius + p2.radius){\n                p1.x = position ? position.x : Math.random() * pJS.canvas.w;\n                p1.y = position ? position.y : Math.random() * pJS.canvas.h;\n                pJS.fn.vendors.checkOverlap(p1);\n            }\n        }\n    };\n\n\n    pJS.fn.vendors.createSvgImg = function(p){\n\n        /* set color to svg element */\n        var svgXml = pJS.tmp.source_svg,\n            rgbHex = /#([0-9A-F]{3,6})/gi,\n            coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) {\n                if(p.color.rgb){\n                    var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')';\n                }else{\n                    var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')';\n                }\n                return color_value;\n            });\n\n        /* prepare to create img with colored svg */\n        var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}),\n            DOMURL = window.URL || window.webkitURL || window,\n            url = DOMURL.createObjectURL(svg);\n\n        /* create particle img obj */\n        var img = new Image();\n        img.addEventListener('load', function(){\n            p.img.obj = img;\n            p.img.loaded = true;\n            DOMURL.revokeObjectURL(url);\n            pJS.tmp.count_svg++;\n        });\n        img.src = url;\n\n    };\n\n\n    pJS.fn.vendors.destroypJS = function(){\n        cancelAnimationFrame(pJS.fn.drawAnimFrame);\n        canvas_el.remove();\n        pJSDom = null;\n    };\n\n\n    pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){\n\n        // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/\n        var sideCount = sideCountNumerator * sideCountDenominator;\n        var decimalSides = sideCountNumerator / sideCountDenominator;\n        var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides;\n        var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians\n        c.save();\n        c.beginPath();\n        c.translate(startX, startY);\n        c.moveTo(0,0);\n        for (var i = 0; i < sideCount; i++) {\n            c.lineTo(sideLength,0);\n            c.translate(sideLength,0);\n            c.rotate(interiorAngle);\n        }\n        //c.stroke();\n        c.fill();\n        c.restore();\n\n    };\n\n    pJS.fn.vendors.exportImg = function(){\n        window.open(pJS.canvas.el.toDataURL('image/png'), '_blank');\n    };\n\n\n    pJS.fn.vendors.loadImg = function(type){\n\n        pJS.tmp.img_error = undefined;\n\n        if(pJS.particles.shape.image.src != ''){\n\n            if(type == 'svg'){\n\n                var xhr = new XMLHttpRequest();\n                xhr.open('GET', pJS.particles.shape.image.src);\n                xhr.onreadystatechange = function (data) {\n                    if(xhr.readyState == 4){\n                        if(xhr.status == 200){\n                            pJS.tmp.source_svg = data.currentTarget.response;\n                            pJS.fn.vendors.checkBeforeDraw();\n                        }else{\n                            console.log('Error pJS - Image not found');\n                            pJS.tmp.img_error = true;\n                        }\n                    }\n                }\n                xhr.send();\n\n            }else{\n\n                var img = new Image();\n                img.addEventListener('load', function(){\n                    pJS.tmp.img_obj = img;\n                    pJS.fn.vendors.checkBeforeDraw();\n                });\n                img.src = pJS.particles.shape.image.src;\n\n            }\n\n        }else{\n            console.log('Error pJS - No image.src');\n            pJS.tmp.img_error = true;\n        }\n\n    };\n\n\n    pJS.fn.vendors.draw = function(){\n\n        if(pJS.particles.shape.type == 'image'){\n\n            if(pJS.tmp.img_type == 'svg'){\n\n                if(pJS.tmp.count_svg >= pJS.particles.number.value){\n                    pJS.fn.particlesDraw();\n                    if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n                    else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n                }else{\n                    //console.log('still loading...');\n                    if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n                }\n\n            }else{\n\n                if(pJS.tmp.img_obj != undefined){\n                    pJS.fn.particlesDraw();\n                    if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n                    else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n                }else{\n                    if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n                }\n\n            }\n\n        }else{\n            pJS.fn.particlesDraw();\n            if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);\n            else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);\n        }\n\n    };\n\n\n    pJS.fn.vendors.checkBeforeDraw = function(){\n\n        // if shape is image\n        if(pJS.particles.shape.type == 'image'){\n\n            if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){\n                pJS.tmp.checkAnimFrame = requestAnimFrame(check);\n            }else{\n                //console.log('images loaded! cancel check');\n                cancelRequestAnimFrame(pJS.tmp.checkAnimFrame);\n                if(!pJS.tmp.img_error){\n                    pJS.fn.vendors.init();\n                    pJS.fn.vendors.draw();\n                }\n\n            }\n\n        }else{\n            pJS.fn.vendors.init();\n            pJS.fn.vendors.draw();\n        }\n\n    };\n\n\n    pJS.fn.vendors.init = function(){\n\n        /* init canvas + particles */\n        pJS.fn.retinaInit();\n        pJS.fn.canvasInit();\n        pJS.fn.canvasSize();\n        pJS.fn.canvasPaint();\n        pJS.fn.particlesCreate();\n        pJS.fn.vendors.densityAutoParticles();\n\n        /* particles.line_linked - convert hex colors to rgb */\n        pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color);\n\n    };\n\n\n    pJS.fn.vendors.start = function(){\n\n        if(isInArray('image', pJS.particles.shape.type)){\n            pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3);\n            pJS.fn.vendors.loadImg(pJS.tmp.img_type);\n        }else{\n            pJS.fn.vendors.checkBeforeDraw();\n        }\n\n    };\n\n\n\n\n    /* ---------- pJS - start ------------ */\n\n\n    pJS.fn.vendors.eventsListeners();\n\n    pJS.fn.vendors.start();\n\n\n\n};\n\n/* ---------- global functions - vendors ------------ */\n\nObject.deepExtend = function(destination, source) {\n    for (var property in source) {\n        if (source[property] && source[property].constructor &&\n            source[property].constructor === Object) {\n            destination[property] = destination[property] || {};\n            arguments.callee(destination[property], source[property]);\n        } else {\n            destination[property] = source[property];\n        }\n    }\n    return destination;\n};\n\nwindow.requestAnimFrame = (function(){\n    return  window.requestAnimationFrame ||\n        window.webkitRequestAnimationFrame ||\n        window.mozRequestAnimationFrame    ||\n        window.oRequestAnimationFrame      ||\n        window.msRequestAnimationFrame     ||\n        function(callback){\n            window.setTimeout(callback, 1000 / 60);\n        };\n})();\n\nwindow.cancelRequestAnimFrame = ( function() {\n    return window.cancelAnimationFrame         ||\n        window.webkitCancelRequestAnimationFrame ||\n        window.mozCancelRequestAnimationFrame    ||\n        window.oCancelRequestAnimationFrame      ||\n        window.msCancelRequestAnimationFrame     ||\n        clearTimeout\n} )();\n\nfunction hexToRgb(hex){\n    // By Tim Down - http://stackoverflow.com/a/5624139/3493650\n    // Expand shorthand form (e.g. \"03F\") to full form (e.g. \"0033FF\")\n    var shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n    hex = hex.replace(shorthandRegex, function(m, r, g, b) {\n        return r + r + g + g + b + b;\n    });\n    var result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n    return result ? {\n        r: parseInt(result[1], 16),\n        g: parseInt(result[2], 16),\n        b: parseInt(result[3], 16)\n    } : null;\n};\n\nfunction clamp(number, min, max) {\n    return Math.min(Math.max(number, min), max);\n};\n\nfunction isInArray(value, array) {\n    return array.indexOf(value) > -1;\n}\n\n\n/* ---------- particles.js functions - start ------------ */\n\nwindow.pJSDom = [];\n\nwindow.particlesJS = function(tag_id, params){\n\n    //console.log(params);\n\n    /* no string id? so it's object params, and set the id with default id */\n    if(typeof(tag_id) != 'string'){\n        params = tag_id;\n        tag_id = 'particles-js';\n    }\n\n    /* no id? set the id to default id */\n    if(!tag_id){\n        tag_id = 'particles-js';\n    }\n\n    /* pJS elements */\n    var pJS_tag = document.getElementById(tag_id),\n        pJS_canvas_class = 'particles-js-canvas-el',\n        exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class);\n\n    /* remove canvas if exists into the pJS target tag */\n    if(exist_canvas.length){\n        while(exist_canvas.length > 0){\n            pJS_tag.removeChild(exist_canvas[0]);\n        }\n    }\n\n    /* create canvas element */\n    var canvas_el = document.createElement('canvas');\n    canvas_el.className = pJS_canvas_class;\n\n    /* set size canvas */\n    canvas_el.style.width = \"100%\";\n    canvas_el.style.height = \"100%\";\n\n    /* append canvas */\n    var canvas = document.getElementById(tag_id).appendChild(canvas_el);\n\n    /* launch particle.js */\n    if(canvas != null){\n        pJSDom.push(new pJS(tag_id, params));\n    }\n\n};\n\nwindow.particlesJS.load = function(tag_id, path_config_json, callback){\n\n    /* load json config */\n    var xhr = new XMLHttpRequest();\n    xhr.open('GET', path_config_json);\n    xhr.onreadystatechange = function (data) {\n        if(xhr.readyState == 4){\n            if(xhr.status == 200){\n                var params = JSON.parse(data.currentTarget.response);\n                window.particlesJS(tag_id, params);\n                if(callback) callback();\n            }else{\n                console.log('Error pJS - XMLHttpRequest status: '+xhr.status);\n                console.log('Error pJS - File config not found');\n            }\n        }\n    };\n    xhr.send();\n\n};"
  },
  {
    "path": "src/particlesjs-config.json",
    "content": "{\n  \"particles\": {\n    \"number\": {\n      \"value\": 30,\n      \"density\": {\n        \"enable\": true,\n        \"value_area\": 500\n      }\n    },\n    \"color\": {\n      \"value\": \"#a6387b\"\n    },\n    \"shape\": {\n      \"type\": \"circle\",\n      \"stroke\": {\n        \"width\": 0,\n        \"color\": \"#000000\"\n      },\n      \"polygon\": {\n        \"nb_sides\": 5\n      },\n      \"image\": {\n        \"src\": \"img/github.svg\",\n        \"width\": 100,\n        \"height\": 100\n      }\n    },\n    \"opacity\": {\n      \"value\": 0.5,\n      \"random\": false,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 1,\n        \"opacity_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"size\": {\n      \"value\": 3,\n      \"random\": true,\n      \"anim\": {\n        \"enable\": false,\n        \"speed\": 5,\n        \"size_min\": 0.1,\n        \"sync\": false\n      }\n    },\n    \"line_linked\": {\n      \"enable\": true,\n      \"distance\": 150,\n      \"color\": \"#a6387b\",\n      \"opacity\": 0.1008530152163807,\n      \"width\": 0.5\n    },\n    \"move\": {\n      \"enable\": true,\n      \"speed\": 2,\n      \"direction\": \"none\",\n      \"random\": false,\n      \"straight\": false,\n      \"out_mode\": \"out\",\n      \"bounce\": false,\n      \"attract\": {\n        \"enable\": false,\n        \"rotateX\": 600,\n        \"rotateY\": 1200\n      }\n    }\n  },\n  \"interactivity\": {\n    \"detect_on\": \"window\",\n    \"events\": {\n      \"onhover\": {\n        \"enable\": true,\n        \"mode\": \"grab\"\n      },\n      \"onclick\": {\n        \"enable\": false,\n        \"mode\": \"push\"\n      },\n      \"resize\": true\n    },\n    \"modes\": {\n      \"grab\": {\n        \"distance\": 400,\n        \"line_linked\": {\n          \"opacity\": 1\n        }\n      },\n      \"bubble\": {\n        \"distance\": 400,\n        \"size\": 40,\n        \"duration\": 2,\n        \"opacity\": 8,\n        \"speed\": 3\n      },\n      \"repulse\": {\n        \"distance\": 200,\n        \"duration\": 0.4\n      },\n      \"push\": {\n        \"particles_nb\": 4\n      },\n      \"remove\": {\n        \"particles_nb\": 2\n      }\n    }\n  },\n  \"retina_detect\": true\n}"
  },
  {
    "path": "src/style.css",
    "content": "* {\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n\nhtml {\n}\n\nbody {\n    background: rgba(0, 0, 0, .1);\n}\n\nh2 {\n    color: #555;\n    font-weight: bold;\n}\n\na {\n    /*margin-right: .5rem;*/\n    text-decoration: none;\n    cursor: pointer;\n}\n\na:hover {\n    text-decoration: underline;\n}\n\nul > li {\n    list-style: none;\n}\n\n.mdl-layout__container {\n    top: 0;\n}\n\n.mdl-navigation {\n    padding: 0 1rem;\n}\n\n.page-content > .mdl-grid {\n    padding-top: 3rem;\n}\n\n.mdl-cell {\n    /*overflow-y: auto;*/\n}\n\n.mdl-card {\n    min-height: 240px;\n    margin: 0 auto;\n    width: 100%;\n    position: relative;\n    z-index: 2;\n    background-size: cover;\n    overflow: hidden;\n}\n\n.mdl-card__title {\n    max-width: 80%;\n    padding-bottom: 0;\n}\n\n.mdl-card__title > h3 {\n    min-height: 60px;\n    display: inline-block;\n    font-weight: bold;\n    color: white;\n    position: relative;\n    z-index: 2;\n    text-shadow: 0 0 5px black;\n}\n\n.mdl-card__supporting-text {\n    color: #fff;\n    padding: 0 1rem;\n    position: relative;\n    z-index: 2;\n}\n\n.mdl-card__supporting-text > span {\n    padding-top: .5rem;\n}\n\n.mdl-card__supporting-text > span.authors {\n    text-shadow: 0 0 5px black;\n}\n\n.mdl-card__supporting-text .tag {\n    margin-bottom: .5rem;\n    margin-right: .5rem;\n}\n\n.mdl-card__supporting-text i.material-icons {\n    font-size: 20px;\n    position: relative;\n    top: 0.35rem;\n}\n\n.mdl-card__supporting-text > .mdl-grid {\n    padding: 0;\n}\n\n.mdl-card--bottom {\n    bottom: 0;\n    left: 0;\n    min-height: 75px;\n    padding-top: 1rem !important;\n    position: absolute;\n}\n\n.mdl-card__menu {\n    background: white;\n    border-radius: 2rem;\n}\n\n.mdl-card__actions {\n    background: rgba(0,0,0, 0.5);\n    font-size: 14px;\n    padding: .5rem 1rem;\n    color: white;\n}\n\n.background {\n    background-color: hsla(0,0%,0%,0.70);\n    background-blend-mode: overlay;\n    min-height: 300px;\n    padding-bottom: 75px;\n    background-size: cover;\n    color: #fff;\n    position: relative;\n    overflow: hidden;\n}\n.background:before {\n    content: '';\n    left: 0;\n    top: 0;\n    z-index: 0;\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    background: linear-gradient(to bottom right, rgba(0,0,0,.1), transparent);\n}\n\n.right {\n    text-align: right;\n}\n\n.particles-js-canvas-el {\n    position: fixed;\n    pointer-events: none;\n    top: 0;\n    left: 0;\n    z-index: -1;\n}\n\n.illustration {\n    background: url(./illustration.svg);\n    min-width: 100%;\n    height: 100%;\n    max-height: 370px;\n    background-repeat: no-repeat;\n    background-position-x: right;\n}\n\n.tags-wrapper {\n    margin: 2rem auto;\n    max-width: 70vw;\n    position: relative;\n    z-index: 3;\n    text-align: left;\n}\n.tags-wrapper > .tag {\n    margin-bottom: .5rem;\n    margin-right: .5rem;\n}\n\n.tag {\n    font-weight: bold;\n    display: inline-block;\n    background: rgba(255, 255, 255, 1);\n    border: 1px solid rgb(255, 64, 129);\n    color: rgb(255, 64, 129);\n    padding: 0.15rem .75rem;\n    border-radius: 1rem;\n    text-decoration: none;\n    font-size: 11px;\n    white-space: nowrap;\n    margin-bottom: .5rem;\n}\n\n.tag:hover {\n    color: white;\n    background: rgba(255, 64, 129, 0.75);\n    text-decoration: none;\n}\n\n.tag.active {\n    color: white;\n    background: rgb(255, 64, 129);\n    text-decoration: none;\n}\n\n.delete {\n    position: relative;\n    top: .25rem;\n    font-size: 16px;\n}\n\nfooter {\n    position: relative;\n    z-index: 3;\n    /*margin-top: 3rem;*/\n}\n\n@media screen and (max-width: 840px){\n    .illustration{\n        height: 150px;\n        width: 100%;\n        background-position-x: center;\n    }\n}\n@media screen and (max-width: 505px){\n    .illustration{\n        height: 150px;\n        background-position-x: center;\n    }\n    .mdl-layout__header-row{\n        padding: 0 16px;\n    }\n    .mdl-layout-title{\n        text-align: left;\n    }\n    h2{\n        font-size: 30px;\n        line-height: 30px;\n    }\n    .tags-wrapper{\n        text-align: center;\n    }\n    .mdl-mini-footer--link-list, .mdl-mini-footer__link-list{\n        flex-direction: column;\n    }\n    .mdl-mini-footer--link-list, .mdl-mini-footer__link-list > li{\n        margin-bottom: 1rem;\n    }\n    footer.ul>li{\n        display: block;\n    }\n}\n"
  },
  {
    "path": "websites.yaml",
    "content": "- name: \"CNN Explainer\"\n  desc: \"An interactive visualization system designed to help non-experts learn about Convolutional Neural Networks (CNNs)\"\n  url: https://poloclub.github.io/cnn-explainer/\n  repo: https://github.com/poloclub/cnn-explainer\n  license: MIT\n  writeup: https://arxiv.org/abs/2004.15004\n  authors: [Zijie Jay Wang, Robert Turko, Omar Shaikh, Haekyu Park, Nilaksh Das, Fred Hohman, Minsuk Kahng, Duen Horng (Polo) Chau]\n  tags: [cnn, convnet]\n  uses: [demo, tutorial]\n  img: https://i.imgur.com/7FGSR7S.png\n\n- name: \"Deep Recurrent Nets character generation demo\"\n  desc: \"This demo shows usage of the recurrentjs library train RNN and LSTM networks in JavaScript.\"\n  url: https://cs.stanford.edu/people/karpathy/recurrentjs/\n  repo: https://github.com/karpathy/recurrentjs\n  license: MIT\n  writeup: http://karpathy.github.io/2015/05/21/rnn-effectiveness/\n  authors: [Andrej Karpathy]\n  tags: [rnn, lstm, nlp]\n  uses: [demo, library]\n  img: https://cs.stanford.edu/people/karpathy/recurrentjs/eg.png\n\n- name: \"ConvNetJS\"\n  desc: \"Various demos of deep learning in JavaScript, using ConvNetJS library.\"\n  url: https://cs.stanford.edu/people/karpathy/convnetjs/\n  repo: https://github.com/karpathy/convnetjs\n  license: MIT\n  writeup: \"\"\n  authors: [Andrej Karpathy]\n  tags: [convnet]\n  uses: [demo, library]\n  img: https://cs.stanford.edu/people/karpathy/convnetjs/cifar10.jpeg\n\n- name: \"Support Vector Machine in JavaScript\"\n  desc: \"A lightweight implementation of the sequential minimal optimization algorithm to train a binary SVM model in JavaScript.\"\n  url: https://cs.stanford.edu/~karpathy/svmjs/demo/\n  repo: https://github.com/karpathy/svmjs\n  license: MIT\n  writeup: \"\"\n  authors: [Andrej Karpathy]\n  tags: [svm]\n  uses: [demo, library, npm]\n  img: imgs/svmjs.png\n\n- name: \"tSNEJS demo\"\n  desc: \"tSNE used for clustering the 500 most-followed accounts on Twitter.\"\n  url: https://cs.stanford.edu/people/karpathy/tsnejs/\n  repo: https://github.com/karpathy/tsnejs\n  license: MIT\n  writeup: http://karpathy.github.io/2014/07/02/visualizing-top-tweeps-with-t-sne-in-Javascript/\n  authors: [Andrej Karpathy]\n  tags: [tsne]\n  uses: [demo, library]\n  img: https://karpathy.github.io/assets/tsne_preview.jpeg\n\n- name: \"Random Forest demo in JavaScript\"\n  desc: \"Random Forest implementation for JavaScript. Supports arbitrary weak learners.\"\n  url: https://cs.stanford.edu/~karpathy/svmjs/demo/demoforest.html\n  repo: https://github.com/karpathy/forestjs\n  license: MIT\n  writeup: \"\"\n  authors: [Andrej Karpathy]\n  tags: [tsne]\n  uses: [demo, library]\n  img: imgs/forestjs.png\n\n- name: \"Keras.js\"\n  desc: \"Keras.js is a library enabling running Keras models in the browser, with GPU support provided by WebGL 2.\"\n  url: https://transcranial.github.io/keras-js/#/\n  repo: https://github.com/transcranial/keras-js\n  license: MIT\n  writeup: \"\"\n  authors: [Leon Chen]\n  tags: [convnet, rnn]\n  uses: [demo, library]\n  img: https://transcranial.github.io/keras-js/demos/assets/resnet50.png\n\n- name: \"Interactive visualization of word analogies in GloVe\"\n  desc: \"king - man + woman is queen; but why? Explore word vectors interactively.\"\n  url: https://lamyiowce.github.io/word2viz/\n  repo: https://github.com/lamyiowce/word2viz\n  license: MIT\n  writeup: http://p.migdal.pl/2017/01/06/king-man-woman-queen-why.html\n  authors: [Julia Bazińska, Piotr Migdał]\n  tags: [nlp, word2vec]\n  uses: [demo]\n  img: https://lamyiowce.github.io/word2viz/word2viz_screenshot.png\n\n- name: \"A Neural Network Playground\"\n  desc: \"Interactive visualization of simple neural networks, written in typescript using d3.js.\"\n  url: http://playground.tensorflow.org/\n  repo: https://github.com/tensorflow/playground\n  license: Apache\n  writeup: \"\"\n  authors: [Daniel Smilkov, Shan Carter]\n  tags: [mlp]\n  uses: [demo]\n  img: https://playground.tensorflow.org/preview.png\n\n- name: \"A visual introduction to machine learning (Part 1)\"\n  url: http://www.r2d3.us/visual-intro-to-machine-learning-part-1/\n  desc: \"A demo of using a decision tree algorithm to distinguish houses in New York from houses in San Francisco\"\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Stephanie Yee, Tony Chu]\n  tags: [decision-tree]\n  uses: [demo, tutorial]\n  img: https://www.r2d3.us/static/pages/decision-trees-part-1/preview-en.png\n\n- name: \"A visual introduction to machine learning (Part 2)\"\n  desc: Model Tuning and the Bias-Variance Tradeoff\n  url: http://www.r2d3.us/visual-intro-to-machine-learning-part-2/\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Stephanie Yee, Tony Chu]\n  tags: [decision-tree, overfitting]\n  uses: [demo, tutorial]\n  img: https://www.r2d3.us/static/pages/decision-trees-part2-v2/part2-preview-en.png\n\n- name: \"D3 Graph Theory\"\n  desc: \"Learn graph theory using interactive visualizations made in D3.js.\"\n  url: https://mrpandey.github.io/d3graphTheory/index.html\n  repo: https://github.com/mrpandey/d3graphTheory\n  license: GPL\n  writeup: \"\"\n  authors: [Avinash Pandey]\n  tags: [graph]\n  uses: [demo, tutorial]\n  img: https://i.imgur.com/MAvlQgj.png\n\n- name: \"Immersive linear algebra\"\n  desc: \"The world's first linear algebra book with fully interactive figures.\"\n  url: http://immersivemath.com/ila/index.html\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [J. Ström, K. Åström, and T. Akenine-Mölle]\n  tags: [math, linear-algebra]\n  uses: [book]\n  img: imgs/immersivemath.png\n\n- name: \"Seeing Theory\"\n  desc: \"A visual introduction to probability and statistics\"\n  url: http://students.brown.edu/seeing-theory/\n  repo: https://github.com/seeingtheory/Seeing-Theory\n  license: Apache\n  writeup: \"\"\n  authors: [Daniel Kunin, Jingru Guo, Tyler Dae Devlin, Daniel Xiang]\n  tags: [statistics]\n  uses: [tutorial]\n  img: https://students.brown.edu/seeing-theory/img/share/home.png\n\n- name: \"tSNE for the Web\"\n  desc: \"TensorFlow.js Powered tSNE Implementation\"\n  url: https://nicola17.github.io/tfjs-tsne-demo/\n  repo: https://github.com/tensorflow/tfjs-tsne\n  license: Apache\n  writeup: https://ai.googleblog.com/2018/06/realtime-tsne-visualizations-with.html\n  authors: [Nicola Pezzotti, Alexander Mordvintsev, Thomas Hollt, Boudewijn P. F. Lelieveldt, Elmar Eisemann, Anna Vilanova]\n  tags: [tsne]\n  uses: [demo, tensorflow.js, npm]\n  img: https://3.bp.blogspot.com/-NE01azL_JxU/Wxli17oYNzI/AAAAAAAACxQ/axOI2yy-Ft0QbqaekOyemm5Xn0wAFvRUwCLcBGAs/s1600/image2.gif\n\n- name: \"GRAIL Text Recognizer\"\n  desc: \"An active essay revisiting Gabriel Groner's GRAIL handwriting recognizer from the 1960s\"\n  url: https://jackschaedler.github.io/handwriting-recognition/\n  repo: https://github.com/jackschaedler/handwriting-recognition\n  license: MIT\n  writeup: \"\"\n  authors: [Jack Schaedler]\n  tags: [handwriting]\n  uses: [demo, tutorial]\n  img: imgs/handwriting-recognition.png\n\n- name: \"pix2pix: Image-to-Image Demo\"\n  desc: \"Tensorflow port of Image-to-Image Translation with Conditional Adversarial Nets\"\n  url: https://affinelayer.com/pixsrv/\n  repo: https://github.com/affinelayer/pix2pix-tensorflow\n  license: MIT\n  writeup: \"\"\n  authors: [Christopher Hesse]\n  tags: [gan, deep-learning]\n  uses: [demo, backend-dependent]\n  img: https://raw.githubusercontent.com/affinelayer/pix2pix-tensorflow/master/docs/examples.jpg\n\n- name: \"nlp-compromise\"\n  desc: \"Modest natural-language processing in JavaScript\"\n  url: http://compromise.cool/\n  repo: https://github.com/spencermountain/compromise\n  license: MIT\n  writeup: \"\"\n  authors: [Spencer Kelly]\n  tags: [nlp]\n  uses: [demo, library]\n  img: imgs/compromise.png\n\n- name: \"NeuroJS\"\n  desc: \"A JavaScript deep learning and reinforcement learning library\"\n  url: https://janhuenermann.com/projects/learning-to-drive\n  repo: https://github.com/janhuenermann/neurojs\n  license: MIT\n  writeup: \"\"\n  authors: [Jan Hünermann]\n  tags: [deep-learning, reinforcement-learning]\n  uses: [demo, library]\n  img: https://raw.githubusercontent.com/janhuenermann/neurojs/experimental/images/logo-with-demo.png\n\n- name: \"DeepForge\"\n  desc: \"A modern development environment for deep learning\"\n  url: http://deepforge.org/\n  repo: https://github.com/deepforge-dev/deepforge\n  license: Apache\n  writeup: \"\"\n  authors: [Brian Broll]\n  tags: [deep-learning]\n  uses: [demo, library]\n  img: https://raw.githubusercontent.com/deepforge-dev/deepforge/master/images/overview.png\n\n- name: \"synaptic\"\n  desc: \"The javascript architecture-free neural network library for node.js and the browser\"\n  url: http://caza.la/synaptic/\n  repo: https://github.com/cazala/synaptic\n  license: MIT\n  writeup: \"\"\n  authors: [Brian Broll]\n  tags: [deep-learning]\n  uses: [demo, library]\n  img: imgs/synaptic.png\n\n- name: \"Machine learning library for Node.js\"\n  desc: \"Logistic regression, SVM linear and radial, kNN\"\n  url: http://joonku.com/project/machine_learning\n  repo: https://github.com/junku901/machine_learning\n  license: MIT\n  writeup: \"\"\n  authors: [Joon-Ku Kang]\n  tags: [machine-learning]\n  uses: [demo, library, npm]\n  img: \"\"\n\n- name: \"Neural Slime Volleyball\"\n  desc: \"HTML5-JS Slime Volleyball clone created using ConvNetJS.\"\n  url: http://otoro.net/slimevolley/\n  repo: https://github.com/hardmaru/neuralslimevolley\n  license: GPL\n  writeup: http://blog.otoro.net/2015/03/28/neural-slime-volleyball/\n  authors: [hardmaru]\n  tags: [deep-learning]\n  uses: [demo]\n  img: imgs/slimevolley.png\n\n- name: \"Quick, draw!\"\n  desc: \"Can a neural network learn to recognize your doodling? The game uses the world’s largest doodling dataset.\"\n  url: https://quickdraw.withgoogle.com/\n  repo: \"\"\n  license: \"\"\n  writeup: https://ai.googleblog.com/2017/04/teaching-machines-to-draw.html\n  authors: [hardmaru]\n  tags: [deep-learning]\n  uses: [game, backend-dependent]\n  img: https://1.bp.blogspot.com/-X0o-v6SQXtI/WO6R8X22wlI/AAAAAAAABts/Qbvu5QoVwZIfKB-zEV7Po_Juh9AqEb28gCLcB/s1600/twitter_cover.png\n\n- name: \"SegNet\"\n  desc: \"A Deep Convolutional Encoder-Decoder Architecture for Robust Semantic Pixel-Wise Labelling\"\n  url: https://mi.eng.cam.ac.uk/projects/segnet/#demo\n  repo: https://github.com/alexgkendall/caffe-segnet\n  license: CC BY NC\n  writeup: https://mi.eng.cam.ac.uk/projects/segnet/#research\n  authors: [Alex Kendall, Vijay Badrinarayanan, Roberto Cipolla]\n  tags: [deep-learning, cnn, segmentation]\n  uses: [demo, backend-dependent]\n  img: imgs/segnet.png\n\n- name: \"Caffe Demos\"\n  desc: \"Web image classification demo\"\n  url: http://demo.caffe.berkeleyvision.org/\n  repo: \"\"\n  license: BSD 2-Clause license\n  writeup: http://caffe.berkeleyvision.org/\n  authors: [Yangqing Jia]\n  tags: [deep-learning, cnn, classification, image-classification]\n  uses: [demo, backend-dependent]\n  img: imgs/caffe-demos.png\n\n- name: \"LSTM Music Maker\"\n  desc: \"How evlolved LSTMs improvise on a melody you specify?\"\n  url: https://www.sentient.ai/sentient-labs/ea/lstm-music/\n  repo: \"\"\n  license: \"\"\n  writeup: https://arxiv.org/abs/1803.04439\n  authors: [Aditya Rawal, Risto Miikkulainen]\n  tags: [deep-learning, lstm, music]\n  uses: [demo]\n  img: imgs/lstm-music-maker.png\n\n- name: \"OMNI Draw\"\n  desc: \"How do the learned models perceive the characters you draw?\"\n  url: https://www.sentient.ai/sentient-labs/ea/omni-draw/\n  repo: \"\"\n  license: \"\"\n  writeup: https://arxiv.org/abs/1803.03745\n  authors: [Jason Liang, Elliot Meyerson, Risto Miikkulainen]\n  tags: [deep-learning, cmtr, omniglot]\n  uses: [demo]\n  img: imgs/omni-draw.png\n\n- name: \"Celeb Match\"\n  desc: \"Which celebrity faces match best the attribute values you specify?\"\n  url: https://www.sentient.ai/sentient-labs/ea/celeb-match/\n  repo: \"\"\n  license: \"\"\n  writeup: https://arxiv.org/abs/1803.04062\n  authors: [Elliot Meyerson, Risto Miikkulainen]\n  tags: [deep-learning, pta]\n  uses: [demo]\n  img: imgs/celeb-match.png\n\n- name: \"Neural Titanic\"\n  desc: \"This visualization uses TensorFlow.js to train a neural network on the titanic dataset\"\n  url: https://andrewnetwork.github.io/NeuralTitanic/dist/\n  repo: https://github.com/Andrewnetwork/NeuralTitanic\n  license: MIT\n  writeup: http://kexp.io/intro_tensorflowjs/\n  authors: [Andrew Ribeiro]\n  tags: [mlp, titanic]\n  uses: [demo, tensorflowjs]\n  img: imgs/neural-titanic.png\n\n- name: \"Backpropagation algorithm\"\n  desc: \"The backpropagation algorithm is essential for training large neural networks quickly. This article explains how the algorithm works.\"\n  url: https://google-developers.appspot.com/machine-learning/crash-course/backprop-scroll/\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Daniel Smilkov]\n  tags: [neural-networks, math]\n  uses: [tutorial]\n  img: imgs/backpropagation-algorithm.png\n\n- name: \"Visual Search\"\n  desc: \"This website uses deep neural networks to compute a fingerprint of a query image and retrieves images with similar visual features.\"\n  url: https://thomasdelteil.github.io/VisualSearch_MXNet/\n  repo: \"https://github.com/ThomasDelteil/VisualSearch_MXNet\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Thomas Delteil]\n  tags: [neural-networks, deep-learning, search, mxnet]\n  uses: [tutorial, demo, backend-dependent]\n  img: imgs/visual-search.png\n\n- name: bayes.js\n  desc: \"An implementation of a small MCMC framework and some likelihood functions for doing Bayes stats in the browser.\"\n  url: https://github.com/rasmusab/bayes.js\n  repo: https://github.com/rasmusab/bayes.js\n  license: MIT\n  writeup: \"\"\n  authors: [Rasmus Bååth]\n  tags: [bayesian, mcmc, statistics]\n  uses: [demo, library]\n  img: https://raw.githubusercontent.com/rasmusab/bayes.js/master/media/normal_model_plotly.png\n\n- name: Basic Neural Network Math\n  desc: \"A Visual And Interactive Look at Basic Neural Network Math\"\n  url: https://jalammar.github.io/feedforward-neural-networks-visual-interactive/\n  repo: https://github.com/jalammar/jalammar.github.io/blob/master/_posts/2018-03-23-feedforward-neural-networks-visual-interactive.md\n  license: \"\"\n  writeup: \"\"\n  authors: [Jay Alammar]\n  tags: [neural-networks]\n  uses: [tutorial]\n  img: https://jalammar.github.io/images/two-input-one-output-sigmoid-network.png\n\n- name: Seedbank\n  desc: \"Collection of Interactive Machine Learning Examples\"\n  url: http://tools.google.com/seedbank/\n  repo: \"\"\n  license: \"\"\n  writeup: https://medium.com/tensorflow/seedbank-discover-machine-learning-examples-2ff894542b57\n  authors: [Mike Tyka, Sures Kumar Thoddu Srinivasan, Chris Boudreaux, Simon Doury, Harini Krishnamurthy, Mike Dory, Gabriel Schubiner, Kyle Pedersen, Artists & Machine Intelligence, Colaboratory teams]\n  tags: [neural-networks, deep-learning]\n  uses: [collection, backend-dependent]\n  img: https://tools.google.com/seedbank/static/images/quick-start.gif\n\n- name: \"The Beginner's Guide to Dimensionality Reduction\"\n  desc: \"Explore the methods that data scientists use to visualize high-dimensional data.\"\n  url: https://idyll.pub/post/dimensionality-reduction-293e465c2a3443e8941b016d/\n  repo: https://github.com/mathisonian/dimensionality-reduction\n  license: MIT\n  writeup: \"\"\n  authors: [Matthew Conlen, Fred Hohman]\n  tags: [pca, tsne]\n  uses: [tutorial]\n  img: https://idyll.pub/post/dimensionality-reduction-293e465c2a3443e8941b016d/static/images/share.png\n\n- name: \"Visualizing K-Means Clustering\"\n  desc: \"The k-means algorithm captures the insight that each point in a cluster should be near to the center of that cluster.\"\n  url: https://www.naftaliharris.com/blog/visualizing-k-means-clustering/\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Naftali Harris]\n  tags: [kmeans]\n  uses: [tutorial]\n  img: imgs/visualizing-k-means-clustering.png\n  date: \"2014-01-19\"\n\n- name: \"Matrix factorization visualized\"\n  desc: \"Matrix factorization implemented in pure JavaScript. Also for sparse data and logistic regression.\"\n  url: https://p.migdal.pl/matrix-decomposition-viz/\n  repo: https://github.com/stared/matrix-decomposition-viz\n  license: MIT\n  writeup: \"\"\n  authors: [Piotr Migdał]\n  tags: [matrix-factorization]\n  uses: [demo]\n  img: imgs/matrix-decomposition-viz.png\n  date: \"2017-10-17\"\n\n- name: \"AI Experiments - Experiments with Google\"\n  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.\"\n  url: https://experiments.withgoogle.com/collection/ai\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Google]\n  tags: [matrix-factorization]\n  uses: [collection]\n  img: imgs/experiments-with-google.png\n\n- name: \"A New Angle on L2 Regularization\"\n  desc: \"An explorable explanation on the phenomenon of adversarial examples in linear classification and its relation to L2 regularization.\"\n  url: https://thomas-tanay.github.io/post--L2-regularization/\n  repo: https://github.com/thomas-tanay/post--L2-regularization\n  license: \"\"\n  writeup: \"\"\n  authors: [Thomas Tanay, Lewis D Griffin]\n  tags: [svm, overfitting, adversarial-examples]\n  uses: [tutorial, paper]\n  img: \"imgs/a-new-angle-on-l2-reg.png\"\n  date: \"2018-06-21\"\n\n- name: \"Support Vector Machine Explorer\"\n  desc: \"This is a demo of the Dash interactive Python framework developed by Plotly.\"\n  url: https://dash-svm.plot.ly/\n  repo: https://github.com/plotly/dash-svm\n  license: MIT\n  writeup: \"\"\n  authors: [Xing Han Lu]\n  tags: [svm]\n  uses: [demo, backend-dependent, plotly, scikit-learn]\n  img: imgs/dash-svm-plot-ly.png\n  date: \"2018-08-09\"\n\n- name: \"GAN lab\"\n  desc: \"Play with Generative Adversarial Networks (GANs) in your browser!\"\n  url: https://poloclub.github.io/ganlab/\n  repo: https://github.com/poloclub/ganlab\n  license: Apache\n  writeup: \"\"\n  authors: [Minsuk Kahng, Nikhil Thorat, Polo Chau, Fernanda Viégas, and Martin Wattenberg]\n  tags: [GAN, deep-learning, tensorflowjs]\n  uses: [demo]\n  img: imgs/ganlab.png\n  date: \"2018-09-04\"\n- name: \"Semantic Calculator\"\n  desc: \"Do math with word embeddings!\"\n  url: https://semantic.a9.io/\n  repo: \"\"\n  license: \"\"\n  writeup: \"\"\n  authors: [Max Krieger]\n  tags: [word2vec, glove, word-embeddings]\n  uses: [demo, backend-dependent]\n  img: imgs/semantic-calculator.png\n  date: \"2018-09-29\"\n\n- name: \"ml5.js\"\n  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.\"\n  url: https://ml5js.org/\n  repo: https://github.com/ml5js/ml5-library\n  license: MIT\n  writeup: https://towardsdatascience.com/introduction-to-ml5-js-3fe51d6a4661\n  authors: [NYU ITP teachers, residents and students]\n  tags: [machine-learning, tensorflowjs]\n  uses: [tutorial, demo, library]\n  img: imgs/ml5.png\n\n- name: \"Drum Patterns from Latent Space\"\n  desc: \"A latent explorable space with some recognizable genre areas.\"\n  url: http://altsoph.com/pp/dsp/map.html#\n  repo: https://github.com/altsoph/drum_space\n  license: MIT\n  writeup: https://towardsdatascience.com/drum-patterns-from-latent-space-23d59dd9d827\n  authors: [Aleksey Tikhonov]\n  tags: [music, latent]\n  uses: [demo]\n  img: imgs/drum_space.png\n  date: \"2019-03-10\"\n"
  }
]