├── .gitignore ├── README.md ├── assets ├── favicon │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── mstile-150x150.png │ └── site.webmanifest ├── fonts │ ├── Poppins-Regular.woff │ └── Poppins-Regular.woff2 └── images │ └── .gitkeep ├── index.html ├── package-lock.json ├── package.json ├── scripts ├── app.js ├── events │ ├── Emitter.js │ ├── Raf.js │ ├── Resize.js │ └── index.js ├── gl │ ├── index.js │ └── shaders │ │ ├── quad.frag │ │ └── quad.vert ├── store.js └── utils │ ├── bindAll.js │ ├── index.js │ ├── lerp.js │ ├── map.js │ └── selector.js ├── styles ├── base │ ├── elements.scss │ ├── fonts.scss │ └── reset.scss ├── components │ └── .gitkeep ├── main.scss └── utils │ ├── functions.scss │ ├── responsive.scss │ └── variables.scss └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Dependency directories 10 | node_modules/ 11 | 12 | # Optional npm cache directory 13 | .npm 14 | 15 | # parcel-bundler cache (https://parceljs.org/) 16 | .cache 17 | 18 | # Next.js build output 19 | .next 20 | 21 | # build / generate output 22 | build 23 | dist 24 | 25 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Full screen shader ✨🎨 2 | Template for a full screen shader on the web. 3 | 4 | ## Installation 5 | 6 | Install dependencies: 7 | 8 | ``` 9 | npm install 10 | ``` 11 | 12 | Run local server with hot reloading 13 | 14 | ``` 15 | npm run serve 16 | ``` 17 | 18 | Compile the code for development: 19 | 20 | ``` 21 | npm run dev 22 | ``` 23 | 24 | Create the build: 25 | 26 | ``` 27 | npm run build 28 | ``` 29 | -------------------------------------------------------------------------------- /assets/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /assets/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /assets/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /assets/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /assets/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /assets/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /assets/fonts/Poppins-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/fonts/Poppins-Regular.woff -------------------------------------------------------------------------------- /assets/fonts/Poppins-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/fonts/Poppins-Regular.woff2 -------------------------------------------------------------------------------- /assets/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/assets/images/.gitkeep -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Shader 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "temple", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Mario Carrillo ", 6 | "description": "Template for a full screen shader on the web", 7 | "license": "ISC", 8 | "scripts": { 9 | "clean": "rm -rf dist", 10 | "build": "npm run clean && webpack --mode=production --node-env=production", 11 | "dev": "npm run clean && webpack --mode=development", 12 | "watch": "webpack --watch", 13 | "serve": "webpack serve" 14 | }, 15 | "dependencies": { 16 | "glsl-hsl2rgb": "^1.1.0", 17 | "glsl-map": "^1.0.1", 18 | "glsl-noise": "^0.0.0", 19 | "glsl-rotate": "^1.1.0", 20 | "glslify": "^7.1.1", 21 | "gsap": "^3.7.0", 22 | "include-media": "^1.4.10", 23 | "lodash.debounce": "^4.0.8", 24 | "ogl": "^0.0.73", 25 | "tiny-emitter": "^2.1.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.14.6", 29 | "@babel/plugin-proposal-class-properties": "^7.14.5", 30 | "@babel/preset-env": "^7.14.7", 31 | "babel-loader": "^8.2.2", 32 | "copy-webpack-plugin": "^9.0.0", 33 | "css-loader": "^5.2.6", 34 | "file-loader": "^6.2.0", 35 | "glslify-loader": "^2.0.0", 36 | "html-loader": "^2.1.2", 37 | "html-webpack-plugin": "^5.3.2", 38 | "mini-css-extract-plugin": "^1.6.0", 39 | "path": "^0.12.7", 40 | "postcss-loader": "^6.1.0", 41 | "postcss-preset-env": "^6.7.0", 42 | "raw-loader": "^4.0.2", 43 | "sass": "^1.35.1", 44 | "sass-loader": "^12.1.0", 45 | "webpack": "^5.40.0", 46 | "webpack-cli": "^4.7.2", 47 | "webpack-dev-server": "^3.11.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scripts/app.js: -------------------------------------------------------------------------------- 1 | import Gl from './gl'; 2 | 3 | class App { 4 | constructor() { 5 | this.init(); 6 | } 7 | 8 | init() { 9 | const shader = new Gl(); 10 | } 11 | } 12 | 13 | new App(); -------------------------------------------------------------------------------- /scripts/events/Emitter.js: -------------------------------------------------------------------------------- 1 | import Emitter from 'tiny-emitter'; 2 | 3 | export default new Emitter(); -------------------------------------------------------------------------------- /scripts/events/Raf.js: -------------------------------------------------------------------------------- 1 | import gsap from 'gsap'; 2 | import Emitter from './Emitter'; 3 | 4 | class Raf { 5 | constructor() { 6 | this.tick = this.tick.bind(this); 7 | 8 | this.init(); 9 | } 10 | 11 | tick(time, delta) { 12 | Emitter.emit('tick', { time, delta }); 13 | } 14 | 15 | on() { 16 | gsap.ticker.add(this.tick); 17 | } 18 | 19 | init() { 20 | this.on(); 21 | } 22 | } 23 | 24 | export default new Raf(); -------------------------------------------------------------------------------- /scripts/events/Resize.js: -------------------------------------------------------------------------------- 1 | import Emitter from './Emitter'; 2 | import store from '../store'; 3 | import debounce from 'lodash.debounce'; 4 | 5 | class Resize { 6 | constructor() { 7 | this.resize = this.resize.bind(this); 8 | this.init(); 9 | } 10 | 11 | resize() { 12 | store.bounds.ww = window.innerWidth; 13 | store.bounds.wh = window.innerHeight; 14 | 15 | Emitter.emit('resize'); 16 | } 17 | 18 | on() { 19 | window.addEventListener('resize', debounce(this.resize, 200)); 20 | } 21 | 22 | init() { 23 | this.on(); 24 | } 25 | } 26 | 27 | export default new Resize(); -------------------------------------------------------------------------------- /scripts/events/index.js: -------------------------------------------------------------------------------- 1 | import Events from './Emitter'; 2 | import GlobalRaf from './Raf'; 3 | import GlobalResize from './Resize'; 4 | 5 | export { Events, GlobalRaf, GlobalResize }; -------------------------------------------------------------------------------- /scripts/gl/index.js: -------------------------------------------------------------------------------- 1 | import { Renderer, Triangle, Program, Mesh } from 'ogl'; 2 | 3 | import vertexShader from './shaders/quad.vert'; 4 | import fragmentShader from './shaders/quad.frag'; 5 | 6 | import glsl from 'glslify'; 7 | 8 | import store from '@/store'; 9 | import { Events } from '@/events'; 10 | import { bindAll } from '@/utils'; 11 | 12 | export default class Gl { 13 | constructor() { 14 | this.renderer = new Renderer({ 15 | width: store.bounds.ww, 16 | height: store.bounds.wh, 17 | }); 18 | 19 | this.gl = this.renderer.gl; 20 | 21 | bindAll(this, 'resize', 'update'); 22 | 23 | this.init(); 24 | } 25 | 26 | init() { 27 | this.addToDom(); 28 | this.createMesh(); 29 | this.addEvents(); 30 | } 31 | 32 | addToDom() { 33 | document.body.appendChild(this.gl.canvas); 34 | } 35 | 36 | createMesh() { 37 | // Rather than using a plane (two triangles) to cover the viewport here is a 38 | // triangle that includes -1 to 1 range for 'position', and 0 to 1 range for 'uv'. 39 | // Excess will be out of the viewport. 40 | // https://github.com/oframe/ogl/blob/3a271343c4ccfdd830c2c8cf2e0b3648145b3175/examples/triangle-screen-shader.html#L58 41 | 42 | // position uv 43 | // (-1, 3) (0, 2) 44 | // |\ |\ 45 | // |__\(1, 1) |__\(1, 1) 46 | // |__|_\ |__|_\ 47 | // (-1, -1) (3, -1) (0, 0) (2, 0) 48 | 49 | this.geometry = new Triangle(this.gl); 50 | 51 | this.program = new Program(this.gl, { 52 | vertex: glsl(vertexShader), 53 | fragment: glsl(fragmentShader), 54 | uniforms: { 55 | uTime: { value: 0 }, 56 | }, 57 | }); 58 | 59 | this.mesh = new Mesh(this.gl, { 60 | geometry: this.geometry, 61 | program: this.program 62 | }); 63 | } 64 | 65 | addEvents() { 66 | Events.on('tick', this.update); 67 | Events.on('resize', this.resize); 68 | } 69 | 70 | update({ time }) { 71 | this.program.uniforms.uTime.value = time * 0.05; 72 | 73 | this.renderer.render({ scene: this.mesh }); 74 | } 75 | 76 | resize() { 77 | this.renderer.setSize(window.innerWidth, window.innerHeight); 78 | } 79 | } -------------------------------------------------------------------------------- /scripts/gl/shaders/quad.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | #define PI 3.1415926535897932384626433832795 4 | 5 | uniform float uTime; 6 | 7 | varying vec2 vUv; 8 | 9 | #pragma glslify: noise = require(glsl-noise/simplex/3d) 10 | 11 | void main() { 12 | vec2 uv = vUv * 0.1; 13 | 14 | float t = uTime ; 15 | 16 | vec3 a = vec3(0.5, 0.5, 0.5); 17 | vec3 b = vec3(0.5, 0.5, 0.5); 18 | vec3 c = vec3(2.0, 1.0, 0.0); 19 | vec3 d = vec3(0.50, 0.20, 0.25); 20 | 21 | float n = noise(vec3(uv.x, uv.y, t)); 22 | 23 | vec3 color = a + b * cos(2. * PI * (c * n + d)); 24 | gl_FragColor = vec4(color, 1.); 25 | } -------------------------------------------------------------------------------- /scripts/gl/shaders/quad.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 uv; 2 | attribute vec2 position; 3 | 4 | uniform float uTime; 5 | 6 | varying vec2 vUv; 7 | 8 | void main() { 9 | vUv = uv; 10 | 11 | vec2 pos = position; 12 | 13 | gl_Position = vec4(pos, 0, 1); 14 | } -------------------------------------------------------------------------------- /scripts/store.js: -------------------------------------------------------------------------------- 1 | const store = { 2 | bounds: { 3 | ww: window.innerWidth, 4 | wh: window.innerHeight, 5 | }, 6 | }; 7 | 8 | export default store; -------------------------------------------------------------------------------- /scripts/utils/bindAll.js: -------------------------------------------------------------------------------- 1 | export function bindAll(object) { 2 | const functions = [].slice.call(arguments, 1); 3 | 4 | for (let i = 0; i < functions.length; i++) { 5 | const f = functions[i]; 6 | object[f] = bind(object[f], object); 7 | } 8 | } 9 | 10 | function bind(func, context) { 11 | return function() { 12 | return func.apply(context, arguments); 13 | } 14 | } -------------------------------------------------------------------------------- /scripts/utils/index.js: -------------------------------------------------------------------------------- 1 | export { lerp } from './lerp'; 2 | export { qs, qsa } from './selector'; 3 | export { map } from './map'; 4 | export { bindAll } from './bindAll'; -------------------------------------------------------------------------------- /scripts/utils/lerp.js: -------------------------------------------------------------------------------- 1 | export function lerp(a, b, n) { 2 | return a * (1 - n) + b * n; 3 | } -------------------------------------------------------------------------------- /scripts/utils/map.js: -------------------------------------------------------------------------------- 1 | export function map(value, start1, stop1, start2, stop2) { 2 | return ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2 3 | } -------------------------------------------------------------------------------- /scripts/utils/selector.js: -------------------------------------------------------------------------------- 1 | export const qs = (s, o = document) => o.querySelector(s); 2 | export const qsa = (s, o = document) => o.querySelectorAll(s); -------------------------------------------------------------------------------- /styles/base/elements.scss: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-family: 'Poppins'; 4 | font-size: calc(100vw / 1920 * 10); 5 | 6 | @include media('<=phone') { 7 | font-size: calc(100vw / 375 * 10); 8 | } 9 | } 10 | 11 | body { 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | canvas { 17 | position: fixed; 18 | top: 0; 19 | left: 0; 20 | } -------------------------------------------------------------------------------- /styles/base/fonts.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Poppins'; 3 | src: url('/assets/fonts/Poppins-Regular.woff2') format('woff2'), 4 | url('/assets/fonts/Poppins-Regular.woff') format('woff'); 5 | font-weight: normal; 6 | font-style: normal; 7 | font-display: swap; 8 | } -------------------------------------------------------------------------------- /styles/base/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | 27 | /* HTML5 display-role reset for older browsers */ 28 | article, aside, details, figcaption, figure, 29 | footer, header, hgroup, menu, nav, section { 30 | display: block; 31 | } 32 | 33 | body { 34 | line-height: 1; 35 | } 36 | 37 | ol, ul { 38 | list-style: none; 39 | } 40 | 41 | blockquote, q { 42 | quotes: none; 43 | } 44 | 45 | blockquote:before, blockquote:after, 46 | q:before, q:after { 47 | content: ''; 48 | content: none; 49 | } 50 | 51 | table { 52 | border-collapse: collapse; 53 | border-spacing: 0; 54 | } -------------------------------------------------------------------------------- /styles/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioecg/full-screen-shader/d50cb0535d9de925f0e738e24a5d52607a4523ef/styles/components/.gitkeep -------------------------------------------------------------------------------- /styles/main.scss: -------------------------------------------------------------------------------- 1 | @import './utils/variables'; 2 | @import './utils/responsive'; 3 | 4 | @import './base/fonts'; 5 | @import './base/reset'; 6 | @import './base/elements'; -------------------------------------------------------------------------------- /styles/utils/functions.scss: -------------------------------------------------------------------------------- 1 | @function z($name) { 2 | @if index($z-indexes, $name) { 3 | @return (length($z-indexes) - index($z-indexes, $name)) + 1; 4 | } @else { 5 | @warn 'There is no item "#{$name}" in this list; Choose one of: #{$z-indexes}'; 6 | 7 | @return null; 8 | } 9 | } -------------------------------------------------------------------------------- /styles/utils/responsive.scss: -------------------------------------------------------------------------------- 1 | $breakpoints: ( 2 | 'phone': 768px, 3 | 'tablet': 1024px, 4 | 'desktop': 1440px 5 | ) !default; 6 | 7 | @import '../../node_modules/include-media/dist/include-media.scss'; -------------------------------------------------------------------------------- /styles/utils/variables.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | $black: #000000; 3 | $white: #ffffff; 4 | 5 | // Z-index 6 | $z-indexes:(); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | const isProd = process.env.NODE_ENV == 'production'; 7 | 8 | const appDir = path.join(__dirname, 'scripts'); 9 | const assetsDir = path.join(__dirname, 'assets'); 10 | const stylesDir = path.join(__dirname, 'styles'); 11 | 12 | const config = { 13 | mode: isProd ? 'production' : 'development', 14 | 15 | entry: [ 16 | path.join(appDir, 'app.js'), 17 | path.join(stylesDir, 'main.scss') 18 | ], 19 | 20 | output: { 21 | path: path.resolve(__dirname, 'dist'), 22 | filename: isProd ? '[name].[contenthash].js' : '[name].js', 23 | chunkFilename: isProd ? '[id].css' : '[id].[contenthash].css', 24 | publicPath: '/', 25 | }, 26 | 27 | resolve: { 28 | alias: { 29 | '@': path.resolve(__dirname, 'scripts'), 30 | }, 31 | }, 32 | 33 | devServer: { 34 | open: false, 35 | host: 'localhost', 36 | }, 37 | 38 | plugins: [ 39 | new HtmlWebpackPlugin({ 40 | template: 'index.html', 41 | }), 42 | 43 | new CopyWebpackPlugin({ 44 | patterns: [ 45 | { 46 | from: path.join(assetsDir, 'favicon'), 47 | to: '' 48 | }, 49 | 50 | { 51 | from: path.join(assetsDir, 'images'), 52 | to: 'images/' 53 | } 54 | ], 55 | }), 56 | 57 | new MiniCssExtractPlugin({ 58 | filename: isProd ? '[name].[contenthash].css' : '[name].css', 59 | chunkFilename: isProd ? '[id].css' : '[id].[contenthash].css' 60 | }), 61 | 62 | // Other plugins... 63 | ], 64 | 65 | module: { 66 | rules: [ 67 | // JavaScript 68 | { 69 | test: /\.js$/, 70 | use: { 71 | loader: 'babel-loader', 72 | options: { 73 | presets: ['@babel/preset-env'], 74 | plugins: ['@babel/plugin-proposal-class-properties'] 75 | } 76 | } 77 | }, 78 | 79 | // Sass 80 | { 81 | test: /\.scss$/, 82 | use: [ 83 | { 84 | loader: MiniCssExtractPlugin.loader, 85 | options: { 86 | publicPath: '' 87 | } 88 | }, 89 | 90 | { 91 | loader: 'css-loader' 92 | }, 93 | 94 | { 95 | loader: 'postcss-loader', 96 | options: { 97 | postcssOptions: { 98 | plugins: ['postcss-preset-env'], 99 | }, 100 | }, 101 | }, 102 | 103 | { 104 | loader: 'sass-loader' 105 | }, 106 | ], 107 | }, 108 | 109 | // Image, svg assets 110 | { 111 | test: /\.(svg|png|jpg|gif)$/i, 112 | loader: 'file-loader', 113 | options: { 114 | name: '[name].[ext]', 115 | outputPath: 'images/', 116 | 117 | } 118 | }, 119 | 120 | // Font assets 121 | { 122 | test: /\.(woff|woff2)$/i, 123 | loader: 'file-loader', 124 | options: { 125 | name: '[name].[ext]', 126 | outputPath: 'fonts/' 127 | } 128 | }, 129 | 130 | // Shaders 131 | { 132 | test: /\.(glsl|frag|vert)$/, 133 | loader: 'raw-loader', 134 | exclude: /node_modules/ 135 | }, 136 | 137 | { 138 | test: /\.(glsl|frag|vert)$/, 139 | loader: 'glslify-loader', 140 | exclude: /node_modules/ 141 | } 142 | 143 | // Other loaders... 144 | ], 145 | }, 146 | }; 147 | 148 | module.exports = () => { 149 | if (isProd) { 150 | config.mode = 'production'; 151 | } else { 152 | config.mode = 'development'; 153 | } 154 | 155 | return config; 156 | }; 157 | --------------------------------------------------------------------------------