Edit in JSFiddle

/*! Granim v1.0.5 - https://sarcadass.github.io/granim.js */
! function t(e, s, i) {
    function n(a, r) {
        if (!s[a]) {
            if (!e[a]) {
                var h = "function" == typeof require && require;
                if (!r && h) return h(a, !0);
                if (o) return o(a, !0);
                var c = new Error("Cannot find module '" + a + "'");
                throw c.code = "MODULE_NOT_FOUND", c
            }
            var l = s[a] = {
                exports: {}
            };
            e[a][0].call(l.exports, function(t) {
                var s = e[a][1][t];
                return n(s ? s : t)
            }, l, l.exports, t, e, s, i)
        }
        return s[a].exports
    }
    for (var o = "function" == typeof require && require, a = 0; a < i.length; a++) n(i[a]);
    return n
}({
    1: [function(t, e, s) {
        "use strict";

        function i(t) {
            if (t.element instanceof HTMLCanvasElement) this.canvas = t.element;
            else {
                if ("string" != typeof t.element) throw new Error("The element you used is neither a String, nor a HTMLCanvasElement");
                this.canvas = document.querySelector(t.element)
            }
            if (!this.canvas) throw new Error("`" + t.element + "` could not be found in the DOM");
            this.x1 = 0, this.y1 = 0, this.name = t.name || !1, this.elToSetClassOn = t.elToSetClassOn || "body", this.direction = t.direction || "diagonal", this.isPausedWhenNotInView = t.isPausedWhenNotInView || !1, this.opacity = t.opacity, this.states = t.states, this.stateTransitionSpeed = t.stateTransitionSpeed || 1e3, this.previousTimeStamp = null, this.progress = 0, this.isPaused = !1, this.isPausedBecauseNotInView = !1, this.iscurrentColorsSet = !1, this.context = this.canvas.getContext("2d"), this.channels = {}, this.channelsIndex = 0, this.activeState = t.defaultStateName || "default-state", this.isChangingState = !1, this.activeColors = [], this.activeColorDiff = [], this.activetransitionSpeed = null, this.currentColors = [], this.eventPolyfill(), this.events = {
                start: new CustomEvent("granim:start"),
                end: new CustomEvent("granim:end"),
                gradientChange: function(t) {
                    return new CustomEvent("granim:gradientChange", {
                        detail: {
                            isLooping: t.isLooping,
                            colorsFrom: t.colorsFrom,
                            colorsTo: t.colorsTo,
                            activeState: t.activeState
                        },
                        bubbles: !1,
                        cancelable: !1
                    })
                }
            }, this.callbacks = {
                onStart: "function" == typeof t.onStart && t.onStart,
                onGradientChange: "function" == typeof t.onGradientChange && t.onGradientChange,
                onEnd: "function" == typeof t.onEnd && t.onEnd
            }, this.getDimensions(), this.canvas.setAttribute("width", this.x1), this.canvas.setAttribute("height", this.y1), this.setColors(), this.refreshColors(), window.addEventListener("resize", this.onResize.bind(this)), this.isPausedWhenNotInView ? this.pauseWhenNotInView() : this.animation = requestAnimationFrame(this.animateColors.bind(this)), this.callbacks.onStart && this.callbacks.onStart(), this.canvas.dispatchEvent(this.events.start)
        }
        i.prototype.setColors = t("./setColors.js"), i.prototype.eventPolyfill = t("./eventPolyfill.js"), i.prototype.colorDiff = t("./colorDiff.js"), i.prototype.hexToRgb = t("./hexToRgb.js"), i.prototype.setDirection = t("./setDirection.js"), i.prototype.makeGradient = t("./makeGradient.js"), i.prototype.getDimensions = t("./getDimensions.js"), i.prototype.animateColors = t("./animateColors.js"), i.prototype.getLightness = t("./getLightness.js"), i.prototype.refreshColors = t("./refreshColors.js"), i.prototype.changeState = t("./changeState.js"), i.prototype.pause = t("./pause.js"), i.prototype.play = t("./play.js"), i.prototype.clear = t("./clear.js"), i.prototype.getCurrentColors = t("./getCurrentColors.js"), i.prototype.pauseWhenNotInView = t("./pauseWhenNotInView.js"), i.prototype.onResize = t("./onResize.js"), e.exports = i
    }, {
        "./animateColors.js": 2,
        "./changeState.js": 3,
        "./clear.js": 4,
        "./colorDiff.js": 5,
        "./eventPolyfill.js": 6,
        "./getCurrentColors.js": 7,
        "./getDimensions.js": 8,
        "./getLightness.js": 9,
        "./hexToRgb.js": 10,
        "./makeGradient.js": 11,
        "./onResize.js": 12,
        "./pause.js": 13,
        "./pauseWhenNotInView.js": 14,
        "./play.js": 15,
        "./refreshColors.js": 16,
        "./setColors.js": 17,
        "./setDirection.js": 18
    }],
    2: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e, s, i, n = t - this.previousTimeStamp > 100,
                o = void 0 === this.states[this.activeState].loop || this.states[this.activeState].loop;
            (null === this.previousTimeStamp || n) && (this.previousTimeStamp = t), this.progress = this.progress + (t - this.previousTimeStamp), e = (this.progress / this.activetransitionSpeed * 100).toFixed(2), this.previousTimeStamp = t, this.refreshColors(e), e < 100 ? this.animation = requestAnimationFrame(this.animateColors.bind(this)) : this.channelsIndex < this.states[this.activeState].gradients.length - 2 || o ? (this.isChangingState && (this.activetransitionSpeed = this.states[this.activeState].transitionSpeed || 5e3), this.previousTimeStamp = null, this.progress = 0, this.channelsIndex++, s = !1, this.channelsIndex === this.states[this.activeState].gradients.length - 1 ? s = !0 : this.channelsIndex === this.states[this.activeState].gradients.length && (this.channelsIndex = 0), i = void 0 === this.states[this.activeState].gradients[this.channelsIndex + 1] ? this.states[this.activeState].gradients[0] : this.states[this.activeState].gradients[this.channelsIndex + 1], this.setColors(), this.animation = requestAnimationFrame(this.animateColors.bind(this)), this.callbacks.onGradientChange && this.callbacks.onGradientChange({
                isLooping: s,
                colorsFrom: this.states[this.activeState].gradients[this.channelsIndex],
                colorsTo: i,
                activeState: this.activeState
            }), this.canvas.dispatchEvent(this.events.gradientChange({
                isLooping: s,
                colorsFrom: this.states[this.activeState].gradients[this.channelsIndex],
                colorsTo: i,
                activeState: this.activeState
            }))) : (cancelAnimationFrame(this.animation), this.callbacks.onEnd && this.callbacks.onEnd(), this.canvas.dispatchEvent(new CustomEvent("granim:end")))
        }
    }, {}],
    3: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e, s, i = this;
            this.activeState !== t && (this.isPaused || (this.isPaused = !0, this.pause()), this.channelsIndex = -1, this.activetransitionSpeed = this.stateTransitionSpeed, this.activeColorDiff = [], this.activeColors = this.getCurrentColors(), this.progress = 0, this.previousTimeStamp = null, this.isChangingState = !0, this.states[t].gradients[0].forEach(function(n, o, a) {
                e = i.hexToRgb(i.states[t].gradients[0][o]), s = i.colorDiff(i.activeColors[o], e), i.activeColorDiff.push(s)
            }), this.activeState = t, this.play())
        }
    }, {}],
    4: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            cancelAnimationFrame(this.animation), this.context.clearRect(0, 0, this.x1, this.y1)
        }
    }, {}],
    5: [function(t, e, s) {
        "use strict";
        e.exports = function(t, e) {
            var s, i = [];
            for (s = 0; s < 3; s++) i.push(e[s] - t[s]);
            return i
        }
    }, {}],
    6: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            function t(t, e) {
                e = e || {
                    bubbles: !1,
                    cancelable: !1,
                    detail: void 0
                };
                var s = document.createEvent("CustomEvent");
                return s.initCustomEvent(t, e.bubbles, e.cancelable, e.detail), s
            }
            "function" != typeof window.CustomEvent && (t.prototype = window.Event.prototype, window.CustomEvent = t)
        }
    }, {}],
    7: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            var t, e = [];
            return this.currentColors.forEach(function(s, i, n) {
                for (e.push([]), t = 0; t < 3; t++) e[i].push(s[t])
            }), e
        }
    }, {}],
    8: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            this.x1 = this.canvas.offsetWidth, this.y1 = this.canvas.offsetHeight
        }
    }, {}],
    9: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            var t, e = this.getCurrentColors(),
                s = [],
                i = null;
            return e.forEach(function(t, e, i) {
                s.push(Math.max(t[0], t[1], t[2]))
            }), s.forEach(function(e, n, o) {
                i = null === i ? e : i + e, n === s.length - 1 && (t = Math.round(i / (n + 1)))
            }), t >= 128 ? "light" : "dark"
        }
    }, {}],
    10: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
            t = t.replace(e, function(t, e, s, i) {
                return e + e + s + s + i + i
            });
            var s = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);
            return s ? [parseInt(s[1], 16), parseInt(s[2], 16), parseInt(s[3], 16)] : null
        }
    }, {}],
    11: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            var t, e, s = this.setDirection(),
                i = document.querySelector(this.elToSetClassOn).classList;
            for (this.context.clearRect(0, 0, this.x1, this.y1), t = 0; t < this.currentColors.length; t++) e = t ? (1 / (this.currentColors.length - 1) * t).toFixed(2) : 0, s.addColorStop(e, "rgba(" + this.currentColors[t][0] + ", " + this.currentColors[t][1] + ", " + this.currentColors[t][2] + ", " + this.opacity[t] + ")");
            this.name && ("light" === this.getLightness() ? (i.remove(this.name + "-dark"), i.add(this.name + "-light")) : (i.remove(this.name + "-light"), i.add(this.name + "-dark"))), this.context.fillStyle = s, this.context.fillRect(0, 0, this.x1, this.y1)
        }
    }, {}],
    12: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            this.getDimensions(), this.canvas.setAttribute("width", this.x1), this.canvas.setAttribute("height", this.y1), this.refreshColors()
        }
    }, {}],
    13: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e = "isPausedBecauseNotInView" === t;
            e || (this.isPaused = !0), cancelAnimationFrame(this.animation)
        }
    }, {}],
    14: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            function t(t) {
                e && clearTimeout(e), e = setTimeout(function() {
                    var e = s.canvas.getBoundingClientRect(),
                        i = e.bottom < 0 || e.right < 0 || e.left > window.innerWidth || e.top > window.innerHeight;
                    i ? s.isPaused || s.isPausedBecauseNotInView || (s.isPausedBecauseNotInView = !0, s.pause("isPausedBecauseNotInView")) : s.isPaused && t !== !0 || (s.isPausedBecauseNotInView = !1, s.play("isPausedBecauseNotInView"))
                }, 300)
            }
            var e, s = this;
            window.addEventListener("scroll", t), t(!0)
        }
    }, {}],
    15: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e = "isPausedBecauseNotInView" === t;
            e || (this.isPaused = !1), this.animation = requestAnimationFrame(this.animateColors.bind(this))
        }
    }, {}],
    16: [function(t, e, s) {
        "use strict";
        e.exports = function(t) {
            var e, s, i = this;
            this.activeColors.forEach(function(n, o, a) {
                for (s = 0; s < 3; s++) e = i.activeColors[o][s] + Math.ceil(i.activeColorDiff[o][s] / 100 * t), e <= 255 && e >= 0 && (i.currentColors[o][s] = e)
            }), this.makeGradient()
        }
    }, {}],
    17: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            var t, e, s = this;
            return this.channels[this.activeState] || (this.channels[this.activeState] = []), void 0 !== this.channels[this.activeState][this.channelsIndex] ? (this.activeColors = this.channels[this.activeState][this.channelsIndex].colors, void(this.activeColorDiff = this.channels[this.activeState][this.channelsIndex].colorsDiff)) : (this.channels[this.activeState].push([{}]), this.channels[this.activeState][this.channelsIndex].colors = [], this.channels[this.activeState][this.channelsIndex].colorsDiff = [], this.activeColors = [], this.activeColorDiff = [], this.states[this.activeState].gradients[this.channelsIndex].forEach(function(i, n, o) {
                var a = s.hexToRgb(i),
                    r = s.channels[s.activeState];
                r[s.channelsIndex].colors.push(a), s.activeColors.push(a), s.iscurrentColorsSet || s.currentColors.push(s.hexToRgb(i)), s.channelsIndex === s.states[s.activeState].gradients.length - 1 ? t = s.colorDiff(r[s.channelsIndex].colors[n], r[0].colors[n]) : (e = s.hexToRgb(s.states[s.activeState].gradients[s.channelsIndex + 1][n]), t = s.colorDiff(r[s.channelsIndex].colors[n], e)), r[s.channelsIndex].colorsDiff.push(t), s.activeColorDiff.push(t)
            }), this.activetransitionSpeed = this.states[this.activeState].transitionSpeed || 5e3, void(this.iscurrentColorsSet = !0))
        }
    }, {}],
    18: [function(t, e, s) {
        "use strict";
        e.exports = function() {
            var t = this.context;
            switch (this.direction) {
                default:
                case "diagonal":
                    return t.createLinearGradient(0, 0, this.x1, this.y1);
                case "left-right":
                    return t.createLinearGradient(0, 0, this.x1, 0);
                case "top-bottom":
                    return t.createLinearGradient(this.x1 / 2, 0, this.x1 / 2, this.y1);
                case "radial":
                    return t.createRadialGradient(this.x1 / 2, this.y1 / 2, this.x1 / 2, this.x1 / 2, this.y1 / 2, 0)
            }
        }
    }, {}],
    19: [function(t, e, s) {
        window.Granim = t("./lib/Granim.js")
    }, {
        "./lib/Granim.js": 1
    }]
}, {}, [19]); 

var granimInstance = new Granim({
    element: '#canvas-image',
    direction: 'top-bottom',
    opacity: [1, .5, 0],
    isPausedWhenNotInView: true,
    states : {
        "default-state": {
            gradients: [
                ['#485563', '#29323c', '#29323c'],
                ['#00c6ff', '#0072ff', '#0072ff']
            ],
            transitionSpeed: 10000
        }
    }
});
#canvas-image {
    position: absolute;
    display: block;
    width: 100%;
    height: 100%;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background-image: url("https://unsplash.it/2000/1800?random");
    background-size: cover;
}
<canvas id="canvas-image"></canvas>