diff --git a/404.html b/404.html
index 436a31a699f1a1399eb84e9b00e2fb0f7629fdaf..af049081c78ee4991b79c03e0f54b06024f2cca4 100644
--- a/404.html
+++ b/404.html
@@ -5,7 +5,7 @@
     <title>404 - Page not found</title>
     <link href="/css/error.css" rel="stylesheet">
     <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-    <script src="https://www.mattboldt.com/demos/typed-js/js/typed.custom.js"></script>
+    <script src="/js/typed.js"></script>
 </head>
 <body>
 <!-- Matomo Image Tracker-->
diff --git a/internal/500.php b/internal/500.php
index 6b3cfb8fc5b984aef19041825ff39ef73ecc1611..a9ac979619436f292da41fbdf1d12c0699c13543 100644
--- a/internal/500.php
+++ b/internal/500.php
@@ -9,7 +9,7 @@ function getError500()
           <title>500 - Internal Server error</title>
           <link href="/css/error.css" rel="stylesheet">
           <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
-          <script src="https://www.mattboldt.com/demos/typed-js/js/typed.custom.js"></script>
+          <script src="/js/typed.js"></script>
        </head>
        <body>
           <!-- Matomo Image Tracker-->
diff --git a/js/typed.js b/js/typed.js
new file mode 100644
index 0000000000000000000000000000000000000000..717d638dab6c6e2a86ae53292746efe93d727f51
--- /dev/null
+++ b/js/typed.js
@@ -0,0 +1,308 @@
+// The MIT License (MIT)
+
+// Typed.js | Copyright (c) 2014 Matt Boldt | www.mattboldt.com
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+
+
+!function($){
+
+    "use strict";
+
+    var Typed = function(el, options){
+
+        // chosen element to manipulate text
+        this.el = $(el);
+        // options
+        this.options = $.extend({}, $.fn.typed.defaults, options);
+
+        // text content of element
+        this.text = this.el.text();
+
+        // typing speed
+        this.typeSpeed = this.options.typeSpeed;
+
+        // add a delay before typing starts
+        this.startDelay = this.options.startDelay;
+
+        // backspacing speed
+        this.backSpeed = this.options.backSpeed;
+
+        // amount of time to wait before backspacing
+        this.backDelay = this.options.backDelay;
+
+        // input strings of text
+        this.strings = this.options.strings;
+
+        // character number position of current string
+        this.strPos = 0;
+
+        // current array position
+        this.arrayPos = 0;
+
+        // number to stop backspacing on.
+        // default 0, can change depending on how many chars
+        // you want to remove at the time
+        this.stopNum = 0;
+
+        // Looping logic
+        this.loop = this.options.loop;
+        this.loopCount = this.options.loopCount;
+        this.curLoop = 0;
+
+        // for stopping
+        this.stop = false;
+
+        // All systems go!
+        this.build();
+    };
+
+    Typed.prototype =  {
+
+        constructor: Typed
+
+        , init: function(){
+            // begin the loop w/ first current string (global self.string)
+            // current string will be passed as an argument each time after this
+            var self = this;
+            self.timeout = setTimeout(function() {
+                // Start typing
+                self.typewrite(self.strings[self.arrayPos], self.strPos);
+            }, self.startDelay);
+        }
+
+        , build: function(){
+            // Insert cursor
+            this.cursor = $("<span class=\"typed-cursor\">|</span>");
+            this.el.after(this.cursor);
+            this.init();
+        }
+
+        // pass current string state to each function, types 1 char per call
+        , typewrite: function(curString, curStrPos){
+            // exit when stopped
+            if(this.stop === true)
+                return;
+
+            // varying values for setTimeout during typing
+            // can't be global since number changes each time loop is executed
+            var humanize = Math.round(Math.random() * (100 - 30)) + this.typeSpeed;
+            var self = this;
+
+            // ------------- optional ------------- //
+            // backpaces a certain string faster
+            // ------------------------------------ //
+            if (self.arrayPos == 1){
+                self.stopNum = 3;
+                self.backDelay = 10;
+            }
+            //every other time, delete the whole typed string
+            else{
+                self.stopNum = 0;
+                self.backDelay = self.options.backDelay;
+            }
+
+            // contain typing function in a timeout humanize'd delay
+            self.timeout = setTimeout(function() {
+                // check for an escape character before a pause value
+                // format: \^\d+ eg: ^1000 should be able to print the ^ too using ^^
+                // single ^ are removed from string
+                var charPause = 0;
+                var substr = curString.substr(curStrPos);
+                if (substr.charAt(0) === '^') {
+                    var e = 1;
+                    if(substr.match(/^\^\d+/)) {
+                        substr = substr.replace(/^\^(\d+).*/, "$1");
+                        e += substr.length;
+                        charPause = parseInt(substr);
+                    }
+
+                    // strip out the escape character and pause value so they're not printed
+                    curString = curString.substring(0,curStrPos)+curString.substring(curStrPos+e);
+                }
+
+                // timeout for any pause after a character
+                self.timeout = setTimeout(function() {
+                    if(curStrPos === curString.length) {
+                        // fires callback function
+                        self.options.onStringTyped(self.arrayPos);
+
+                        // is this the final string
+                        if(self.arrayPos === self.strings.length-1) {
+                            // animation that occurs on the last typed string
+                            self.options.callback();
+
+                            self.curLoop++;
+
+                            // quit if we wont loop back
+                            if(self.loop === false || self.curLoop === self.loopCount)
+                                return;
+                        }
+
+                        self.timeout = setTimeout(function(){
+                            self.backspace(curString, curStrPos);
+                        }, self.backDelay);
+                    } else {
+
+                        /* call before functions if applicable */
+                        if(curStrPos === 0)
+                            self.options.preStringTyped(self.arrayPos);
+
+                        // start typing each new char into existing string
+                        // curString: arg, self.text: original text inside element
+                        self.el.text(self.text + curString.substr(0, curStrPos+1));
+
+                        // add characters one by one
+                        curStrPos++;
+                        // loop the function
+                        self.typewrite(curString, curStrPos);
+                    }
+                    // end of character pause
+                }, charPause);
+
+                // humanized value for typing
+            }, humanize);
+
+        }
+
+        , backspace: function(curString, curStrPos){
+            // exit when stopped
+            if (this.stop === true) {
+                return;
+            }
+
+            // varying values for setTimeout during typing
+            // can't be global since number changes each time loop is executed
+            var humanize = Math.round(Math.random() * (100 - 30)) + this.backSpeed;
+            var self = this;
+
+            self.timeout = setTimeout(function() {
+
+                // ----- this part is optional ----- //
+                // check string array position
+                // on the first string, only delete one word
+                // the stopNum actually represents the amount of chars to
+                // keep in the current string. In my case it's 14.
+                // if (self.arrayPos == 1){
+                //  self.stopNum = 14;
+                // }
+                //every other time, delete the whole typed string
+                // else{
+                //  self.stopNum = 0;
+                // }
+
+                // ----- continue important stuff ----- //
+                // replace text with current text + typed characters
+                self.el.text(self.text + curString.substr(0, curStrPos));
+
+                // if the number (id of character in current string) is
+                // less than the stop number, keep going
+                if (curStrPos > self.stopNum){
+                    // subtract characters one by one
+                    curStrPos--;
+                    // loop the function
+                    self.backspace(curString, curStrPos);
+                }
+                    // if the stop number has been reached, increase
+                // array position to next string
+                else if (curStrPos <= self.stopNum) {
+                    self.arrayPos++;
+
+                    if(self.arrayPos === self.strings.length) {
+                        self.arrayPos = 0;
+                        self.init();
+                    } else
+                        self.typewrite(self.strings[self.arrayPos], curStrPos);
+                }
+
+                // humanized value for typing
+            }, humanize);
+
+        }
+
+        // Start & Stop currently not working
+
+        // , stop: function() {
+        //     var self = this;
+
+        //     self.stop = true;
+        //     clearInterval(self.timeout);
+        // }
+
+        // , start: function() {
+        //     var self = this;
+        //     if(self.stop === false)
+        //        return;
+
+        //     this.stop = false;
+        //     this.init();
+        // }
+
+        // Reset and rebuild the element
+        , reset: function(){
+            var self = this;
+            clearInterval(self.timeout);
+            var id = this.el.attr('id');
+            this.el.after('<span id="' + id + '"/>')
+            this.el.remove();
+            this.cursor.remove();
+            // Send the callback
+            self.options.resetCallback();
+        }
+
+    };
+
+    $.fn.typed = function (option) {
+        return this.each(function () {
+            var $this = $(this)
+                , data = $this.data('typed')
+                , options = typeof option == 'object' && option;
+            if (!data) $this.data('typed', (data = new Typed(this, options)));
+            if (typeof option == 'string') data[option]();
+        });
+    };
+
+    $.fn.typed.defaults = {
+        strings: ["These are the default values...", "You know what you should do?", "Use your own!", "Have a great day!"],
+        // typing speed
+        typeSpeed: 0,
+        // time before typing starts
+        startDelay: 0,
+        // backspacing speed
+        backSpeed: 0,
+        // time before backspacing
+        backDelay: 500,
+        // loop
+        loop: false,
+        // false = infinite
+        loopCount: false,
+        // call when done callback function
+        callback: function() {},
+        // starting callback function before each string
+        preStringTyped: function() {},
+        //callback for every typed string
+        onStringTyped: function() {},
+        // callback for reset
+        resetCallback: function() {}
+    };
+
+
+}(window.jQuery);
\ No newline at end of file