├── .gitignore
├── LICENSE
├── README.md
├── example
├── .nojekyll
└── index.html
├── lib
├── Shadify.js
└── Shadify.js.map
├── package-lock.json
├── package.json
├── src
├── Shadify.ts
└── index.ts
├── tsconfig.json
└── 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 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 KÆD3
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shadify
2 | 
3 | [](https://github.com/danielfvm/Shadify/commits/master)
4 | 
5 | [](https://github.com/danielfvm/Shadify/stargazers)
6 |
7 | A library for adding a shader wallpaper to the background of your website or any of your divs.
8 | You can find a live example [here](https://danielfvm.github.io/Shadify/example/) and you can find the code in the `example/` folder.
9 |
10 | 
11 |
12 |
13 | ## Usage
14 | Include shadify in your project.
15 | ```html
16 |
17 | ```
18 |
19 | To add a shader to the background or any other div add `data-shader` with a link to a [glslsandbox.com](https://glslsandbox.com/) shader or to your own shader source file.
20 | ```html
21 |
22 | ...
23 |
24 | ```
25 |
26 | Additionally you can set `data-shader-speed` (1.0 default) and `data-shader-quality` (2.0 default) settings. Here an example with the same shader
27 | but at twice the speed and a quarter the quality.
28 | ```html
29 |
30 | ....
31 |
32 | ```
33 |
34 | ## Features
35 | * Shaders can directly be loaded from [glslsandbox.com](https://glslsandbox.com/)
36 | * Support for setting shader quality and speed
37 | * Supports mouse input
38 | * Attributes can be changed using JavaScript at runtime
39 | * Access the shader uniforms using `myDiv.shadify.getUniform(name)`
40 |
41 | ## Planned
42 | * Support links from `shadertoy.com`
43 | * Improved README.md
44 | * Documentation
45 | * Reuse same shaders
46 |
47 | ## Build library
48 | Run following commands to build this library yourself. You will find the output at `dist/`.
49 | ```bash
50 | git clone https://github.com/danielfvm/Shadify.git
51 | cd Shadify
52 | npm install
53 | npm run build
54 | ```
55 |
--------------------------------------------------------------------------------
/example/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danielfvm/Shadify/3ca18b94491e11baa1f02249771b3d418b39a6fe/example/.nojekyll
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Shadify
7 |
8 |
9 |
10 |
11 |
12 | Welcome to Shadify!
13 |
14 |
Hello World!
15 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
46 |
47 |
59 |
60 |
--------------------------------------------------------------------------------
/lib/Shadify.js:
--------------------------------------------------------------------------------
1 | var Shadify;(()=>{"use strict";var t={336:function(t,e){var i,s=this&&this.__awaiter||function(t,e,i,s){return new(i||(i=Promise))((function(a,r){function n(t){try{o(s.next(t))}catch(t){r(t)}}function h(t){try{o(s.throw(t))}catch(t){r(t)}}function o(t){var e;t.done?a(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(n,h)}o((s=s.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0}),e.Shadify=void 0,function(t){class e{constructor(t,e){if(this.running=!0,this.eventHandlerResize=this.resize.bind(this),this.eventHandlerMouse=this.mouse.bind(this),this._quality=2,this.speed=1,this.target=e,"CANVAS"===this.target.tagName)this.canvas=this.target;else{if(this.canvas=document.createElement("canvas"),e.append(this.canvas),e!==document.body){const t=getComputedStyle(e).borderRadius||0;e.style.clipPath=`inset(0 0 0 0 round ${t})`}this.canvas.style.position="fixed",this.canvas.style.left="0%",this.canvas.style.right="0%",this.canvas.style.top="0%",this.canvas.style.bottom="0%",this.canvas.style.width="100%",this.canvas.style.height="100%"}this.updateAttributes(),this.gl=this.canvas.getContext("webgl")||this.canvas.getContext("experimental-webgl"),this.loadShader(t);let i=new Float32Array([-1,3,-1,-1,3,-1]);this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.gl.createBuffer()),this.gl.bufferData(this.gl.ARRAY_BUFFER,i,this.gl.STATIC_DRAW);let s=this.gl.getAttribLocation(this.pid,"coords");this.gl.vertexAttribPointer(s,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(s),window.addEventListener("resize",this.eventHandlerResize),window.addEventListener("mousemove",this.eventHandlerMouse),new MutationObserver((t=>{this.updateAttributes()})).observe(e,{subtree:!1,childList:!1,attributes:!0,attributeFilter:["data-shader","data-shader-quality","data-shader-speed"]}),this.update()}loadShader(t){this.pid&&this.gl.deleteProgram(this.pid),this.vert&&this.gl.deleteShader(this.vert),this.frag&&this.gl.deleteShader(this.frag),this.pid=this.gl.createProgram(),this.vert=i(this.gl,"\n attribute vec2 coords;\n void main(void) {\n gl_Position = vec4(coords.xy, 0.0, 1.0);\n }\n ",this.gl.VERTEX_SHADER),this.gl.attachShader(this.pid,this.vert),this.frag=i(this.gl,t,this.gl.FRAGMENT_SHADER),this.gl.attachShader(this.pid,this.frag),this.gl.linkProgram(this.pid),this.gl.useProgram(this.pid),this.uniformResolution=this.gl.getUniformLocation(this.pid,"resolution"),this.uniformTime=this.gl.getUniformLocation(this.pid,"time"),this.uniformMouse=this.gl.getUniformLocation(this.pid,"mouse")}updateAttributes(){var t,e,i,s;this.quality=Number(null===(t=this.target.attributes.getNamedItem("data-shader-quality"))||void 0===t?void 0:t.value)||this.quality,this.speed=Number(null===(e=this.target.attributes.getNamedItem("data-shader-speed"))||void 0===e?void 0:e.value)||this.speed,this.canvas.style.zIndex=(null===(i=this.target.attributes.getNamedItem("data-shader-z-index"))||void 0===i?void 0:i.value)||"-1",(null===(s=this.target.attributes.getNamedItem("data-shader"))||void 0===s?void 0:s.value)||this.destroy()}resize(){"CANVAS"!==this.target.tagName&&(this.canvas.width=window.innerWidth/this.quality,this.canvas.height=window.innerHeight/this.quality)}mouse(t){this.mouseX=t.clientX,this.mouseY=t.clientY}update(){const{width:t,height:e}=this.canvas;this.running&&(this.gl.uniform2f(this.uniformMouse,this.mouseX/t/this.quality,1-this.mouseY/e/this.quality),this.gl.uniform2f(this.uniformResolution,t,e),this.gl.uniform1f(this.uniformTime,performance.now()/1e3*this.speed),this.gl.viewport(0,0,t,e),this.gl.clearColor(0,0,0,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.drawArrays(this.gl.TRIANGLES,0,3),requestAnimationFrame(this.update.bind(this)))}getUniform(t){return this.gl.getUniformLocation(this.pid,t)}destroy(){this.target.shadify=void 0,this.running=!1,this.gl.deleteProgram(this.pid),this.gl.deleteShader(this.vert),this.gl.deleteShader(this.frag),this.canvas.remove(),this.pid=null,this.vert=null,this.frag=null,window.removeEventListener("resize",this.eventHandlerResize),window.removeEventListener("mousemove",this.eventHandlerMouse)}get quality(){return this._quality}set quality(t){this._quality=t,this.resize()}}function i(t,e,i){const s=t.createShader(i);if(t.shaderSource(s,e),t.compileShader(s),!t.getShaderParameter(s,t.COMPILE_STATUS))throw`Could not compile WebGL program. \n\n${t.getShaderInfoLog(s)}`;return s}function a(t){return s(this,void 0,void 0,(function*(){if(t.includes("glslsandbox.com")&&t.includes("/e#")){t=t.replace("/e#","/item/");const e=yield fetch(t),i=yield e.json();if(void 0===i.code)throw new Error("Could not get shader code");return i.code}const e=yield fetch(t);return yield e.text()}))}function r(t){var i;return s(this,void 0,void 0,(function*(){if(null==t)return;const s=null===(i=t.attributes.getNamedItem("data-shader"))||void 0===i?void 0:i.value;null!=s&&0!=s.trim().length&&(null!=(null==t?void 0:t.shadify)?t.shadify.target==t?t.shadify.loadShader(yield a(s)):t.shadify.destroy():(console.log(t),t.shadify=new e(yield a(s),t)))}))}document.addEventListener("DOMContentLoaded",(function t(){new MutationObserver((t=>{console.log(t,t.filter((e=>t.filter((t=>t.target==e.target)).length>=t.length-1))),t.filter((e=>t.filter((t=>t.target==e.target)).length>=t.length-1)).forEach((t=>r(t.target)))})).observe(document.body,{subtree:!0,childList:!1,attributeFilter:["data-shader"]}),document.querySelectorAll("[data-shader]").forEach((t=>r(t))),document.removeEventListener("DOMContentLoaded",t)}))}(i||(e.Shadify=i={}))},607:(t,e,i)=>{Object.defineProperty(e,"__esModule",{value:!0});const s=i(336);e.default=s.Shadify,Object.assign(t.exports,s.Shadify)}},e={},i=function i(s){var a=e[s];if(void 0!==a)return a.exports;var r=e[s]={exports:{}};return t[s].call(r.exports,r,r.exports,i),r.exports}(607);Shadify=i})();
2 | //# sourceMappingURL=Shadify.js.map
--------------------------------------------------------------------------------
/lib/Shadify.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"Shadify.js","mappings":"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","sources":["webpack://Shadify/./src/Shadify.ts","webpack://Shadify/./src/index.ts","webpack://Shadify/webpack/bootstrap","webpack://Shadify/webpack/startup"],"sourcesContent":["\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.Shadify = void 0;\nvar Shadify;\n(function (Shadify) {\n const vertShader = `\n attribute vec2 coords;\n void main(void) {\n gl_Position = vec4(coords.xy, 0.0, 1.0);\n }\n `;\n class Wallpaper {\n constructor(fragShader, target) {\n this.running = true;\n this.eventHandlerResize = this.resize.bind(this);\n this.eventHandlerMouse = this.mouse.bind(this);\n this._quality = 2;\n this.speed = 1;\n this.target = target;\n if (this.target.tagName === \"CANVAS\") {\n this.canvas = this.target;\n }\n else {\n this.canvas = document.createElement(\"canvas\");\n target.append(this.canvas);\n // Setting this in body would break the fullscreen look\n if (target !== document.body) {\n const style = getComputedStyle(target);\n const radius = style.borderRadius || 0;\n target.style.clipPath = `inset(0 0 0 0 round ${radius})`;\n }\n this.canvas.style.position = \"fixed\";\n this.canvas.style.left = \"0%\";\n this.canvas.style.right = \"0%\";\n this.canvas.style.top = \"0%\";\n this.canvas.style.bottom = \"0%\";\n this.canvas.style.width = \"100%\";\n this.canvas.style.height = \"100%\";\n }\n // Fetches the attributes and sets quality and speed if given\n this.updateAttributes();\n // Get gl context from canvas\n this.gl = (this.canvas.getContext(\"webgl\") || this.canvas.getContext(\"experimental-webgl\"));\n this.loadShader(fragShader);\n // Create buffer\n let array = new Float32Array([-1, 3, -1, -1, 3, -1]);\n this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.gl.createBuffer());\n this.gl.bufferData(this.gl.ARRAY_BUFFER, array, this.gl.STATIC_DRAW);\n let al = this.gl.getAttribLocation(this.pid, \"coords\");\n this.gl.vertexAttribPointer(al, 2, this.gl.FLOAT, false, 0, 0);\n this.gl.enableVertexAttribArray(al);\n window.addEventListener(\"resize\", this.eventHandlerResize);\n window.addEventListener(\"mousemove\", this.eventHandlerMouse);\n const observer = new MutationObserver((_) => {\n this.updateAttributes();\n });\n observer.observe(target, {\n subtree: false,\n childList: false,\n attributes: true,\n attributeFilter: [\"data-shader\", \"data-shader-quality\", \"data-shader-speed\"],\n });\n this.update();\n }\n loadShader(fragShader) {\n // Before initializing new shader, remove old ones if present\n if (this.pid)\n this.gl.deleteProgram(this.pid);\n if (this.vert)\n this.gl.deleteShader(this.vert);\n if (this.frag)\n this.gl.deleteShader(this.frag);\n // Create shader program\n this.pid = this.gl.createProgram();\n // Compile vertex shader\n this.vert = createShader(this.gl, vertShader, this.gl.VERTEX_SHADER);\n this.gl.attachShader(this.pid, this.vert);\n // Compile fragment shader\n this.frag = createShader(this.gl, fragShader, this.gl.FRAGMENT_SHADER);\n this.gl.attachShader(this.pid, this.frag);\n this.gl.linkProgram(this.pid);\n this.gl.useProgram(this.pid);\n // Read uniforms\n this.uniformResolution = this.gl.getUniformLocation(this.pid, \"resolution\");\n this.uniformTime = this.gl.getUniformLocation(this.pid, \"time\");\n this.uniformMouse = this.gl.getUniformLocation(this.pid, \"mouse\");\n }\n updateAttributes() {\n var _a, _b, _c, _d;\n this.quality = Number((_a = this.target.attributes.getNamedItem(\"data-shader-quality\")) === null || _a === void 0 ? void 0 : _a.value) || this.quality;\n this.speed = Number((_b = this.target.attributes.getNamedItem(\"data-shader-speed\")) === null || _b === void 0 ? void 0 : _b.value) || this.speed;\n this.canvas.style.zIndex = ((_c = this.target.attributes.getNamedItem(\"data-shader-z-index\")) === null || _c === void 0 ? void 0 : _c.value) || \"-1\";\n if (!((_d = this.target.attributes.getNamedItem(\"data-shader\")) === null || _d === void 0 ? void 0 : _d.value)) {\n this.destroy();\n }\n }\n resize() {\n if (this.target.tagName !== \"CANVAS\") {\n this.canvas.width = window.innerWidth / this.quality;\n this.canvas.height = window.innerHeight / this.quality;\n }\n }\n mouse(e) {\n this.mouseX = e.clientX;\n this.mouseY = e.clientY;\n }\n update() {\n const { width, height } = this.canvas;\n if (!this.running)\n return;\n this.gl.uniform2f(this.uniformMouse, this.mouseX / width / this.quality, 1 - this.mouseY / height / this.quality);\n this.gl.uniform2f(this.uniformResolution, width, height);\n this.gl.uniform1f(this.uniformTime, performance.now() / 1000 * this.speed);\n this.gl.viewport(0, 0, width, height);\n this.gl.clearColor(0, 0, 0, 0);\n this.gl.clear(this.gl.COLOR_BUFFER_BIT);\n this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);\n requestAnimationFrame(this.update.bind(this));\n }\n /**\n * Returns a WebGLUniformLocation for the given name in the fragment shader.\n * @param {string} name\n * @returns {WebGLUniformLocation}\n */\n getUniform(name) {\n return this.gl.getUniformLocation(this.pid, name);\n }\n /**\n * This function has to be called to clean up the WebGL context.\n * It is also called when the element is removed from the DOM.\n * @returns {void}\n */\n destroy() {\n this.target.shadify = undefined;\n this.running = false;\n this.gl.deleteProgram(this.pid);\n this.gl.deleteShader(this.vert);\n this.gl.deleteShader(this.frag);\n this.canvas.remove();\n this.pid = null;\n this.vert = null;\n this.frag = null;\n window.removeEventListener(\"resize\", this.eventHandlerResize);\n window.removeEventListener(\"mousemove\", this.eventHandlerMouse);\n }\n get quality() {\n return this._quality;\n }\n set quality(value) {\n this._quality = value;\n this.resize();\n }\n }\n /**\n * Helper function for creating a WebGLShader in Wallpaper class\n * @param {WebGLRenderingContext} gl\n * @param {string} sourceCode\n * @param {number} type\n * @returns {WebGLShader}\n */\n function createShader(gl, sourceCode, type) {\n const shader = gl.createShader(type);\n gl.shaderSource(shader, sourceCode);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader);\n throw `Could not compile WebGL program. \\n\\n${info}`;\n }\n return shader;\n }\n /**\n * Fetches the shader code from the given URL.\n * If URL is from glslsandbox.com, it will fetch the code from the item page.\n * @param {string} url\n * @returns {Promise}\n */\n function getCodeFromURL(url) {\n return __awaiter(this, void 0, void 0, function* () {\n // link to glslsandbox website is a special case\n if (url.includes(\"glslsandbox.com\") && url.includes(\"/e#\")) {\n url = url.replace(\"/e#\", \"/item/\");\n const res = yield fetch(url);\n const text = yield res.json();\n if (typeof text.code === \"undefined\")\n throw new Error(\"Could not get shader code\");\n return text.code;\n }\n // otherwise we expect a link to a source file and return the code\n const res = yield fetch(url);\n return yield res.text();\n });\n }\n /**\n * Helper function for handling elements with the \"data-shader\" attribute.\n * @param {HTMLElement} target\n * @returns {void}\n */\n function handleHTMLElement(target) {\n var _a;\n return __awaiter(this, void 0, void 0, function* () {\n if (target == null)\n return;\n const url = (_a = target.attributes.getNamedItem(\"data-shader\")) === null || _a === void 0 ? void 0 : _a.value;\n if (url == null || url.trim().length == 0)\n return;\n if ((target === null || target === void 0 ? void 0 : target.shadify) != null) {\n if (target.shadify.target == target)\n target.shadify.loadShader(yield getCodeFromURL(url));\n else\n target.shadify.destroy();\n }\n else {\n console.log(target);\n target.shadify = new Wallpaper(yield getCodeFromURL(url), target);\n }\n });\n }\n /**\n * Initializes Shadify.\n * @returns {void}\n */\n function init() {\n const observer = new MutationObserver((mutationList) => {\n console.log(mutationList, mutationList\n .filter(x => mutationList.filter(y => y.target == x.target).length >= mutationList.length - 1));\n mutationList\n .filter(x => mutationList.filter(y => y.target == x.target).length >= mutationList.length - 1) // filter duplicate targets\n .forEach((mutation) => handleHTMLElement(mutation.target));\n });\n observer.observe(document.body, {\n subtree: true,\n childList: false,\n attributeFilter: [\"data-shader\"]\n });\n // Handle elements with the \"data-shader\" attribute\n document.querySelectorAll('[data-shader]').forEach((e) => handleHTMLElement(e));\n document.removeEventListener(\"DOMContentLoaded\", init);\n }\n document.addEventListener(\"DOMContentLoaded\", init);\n})(Shadify || (exports.Shadify = Shadify = {}));\n","\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst Shadify_1 = require(\"./Shadify\");\nexports.default = Shadify_1.Shadify;\nObject.assign(module.exports, Shadify_1.Shadify);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(607);\n"],"names":["Shadify","__awaiter","this","thisArg","_arguments","P","generator","Promise","resolve","reject","fulfilled","value","step","next","e","rejected","result","done","then","apply","Object","defineProperty","exports","Wallpaper","constructor","fragShader","target","running","eventHandlerResize","resize","bind","eventHandlerMouse","mouse","_quality","speed","tagName","canvas","document","createElement","append","body","radius","getComputedStyle","borderRadius","style","clipPath","position","left","right","top","bottom","width","height","updateAttributes","gl","getContext","loadShader","array","Float32Array","bindBuffer","ARRAY_BUFFER","createBuffer","bufferData","STATIC_DRAW","al","getAttribLocation","pid","vertexAttribPointer","FLOAT","enableVertexAttribArray","window","addEventListener","MutationObserver","_","observe","subtree","childList","attributes","attributeFilter","update","deleteProgram","vert","deleteShader","frag","createProgram","createShader","VERTEX_SHADER","attachShader","FRAGMENT_SHADER","linkProgram","useProgram","uniformResolution","getUniformLocation","uniformTime","uniformMouse","_a","_b","_c","_d","quality","Number","getNamedItem","zIndex","destroy","innerWidth","innerHeight","mouseX","clientX","mouseY","clientY","uniform2f","uniform1f","performance","now","viewport","clearColor","clear","COLOR_BUFFER_BIT","drawArrays","TRIANGLES","requestAnimationFrame","getUniform","name","shadify","undefined","remove","removeEventListener","sourceCode","type","shader","shaderSource","compileShader","getShaderParameter","COMPILE_STATUS","getShaderInfoLog","getCodeFromURL","url","includes","replace","res","fetch","text","json","code","Error","handleHTMLElement","trim","length","console","log","init","mutationList","filter","x","y","forEach","mutation","querySelectorAll","Shadify_1","assign","module","__webpack_module_cache__","__webpack_exports__","__webpack_require__","moduleId","cachedModule","__webpack_modules__","call"],"sourceRoot":""}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shadify",
3 | "version": "1.0.1",
4 | "description": "A library for adding shaders to the background of your website.",
5 | "main": "lib/shadify.js",
6 | "scripts": {
7 | "build": "NODE_ENV=production node_modules/.bin/webpack --config webpack.config.js --progress"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/danielfvm/Shadify.git"
12 | },
13 | "keywords": [
14 | "shader",
15 | "wallpaper",
16 | "animations"
17 | ],
18 | "author": "DeanCode",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/danielfvm/Shadify/issues"
22 | },
23 | "homepage": "https://github.com/danielfvm/Shadify#readme",
24 | "devDependencies": {
25 | "ts-jest": "^29.1.1",
26 | "typescript": "^5.2.2",
27 | "webpack-cli": "^5.1.4"
28 | },
29 | "dependencies": {
30 | "ts-loader": "^9.4.4",
31 | "webpack": "^5.88.2"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Shadify.ts:
--------------------------------------------------------------------------------
1 | export namespace Shadify {
2 | const vertShader = `
3 | attribute vec2 coords;
4 | void main(void) {
5 | gl_Position = vec4(coords.xy, 0.0, 1.0);
6 | }
7 | `;
8 |
9 | class Wallpaper {
10 | public gl: WebGLRenderingContext;
11 | public canvas: HTMLCanvasElement;
12 |
13 | private uniformResolution: WebGLUniformLocation;
14 | private uniformTime: WebGLUniformLocation;
15 | private uniformMouse: WebGLUniformLocation;
16 |
17 | private pid: WebGLProgram;
18 | private vert: WebGLShader;
19 | private frag: WebGLShader;
20 |
21 | private target: HTMLElement;
22 |
23 | private mouseX: number;
24 | private mouseY: number;
25 |
26 | private running = true;
27 |
28 | private eventHandlerResize = this.resize.bind(this);
29 | private eventHandlerMouse = this.mouse.bind(this);
30 |
31 | private _quality: number = 2;
32 | public speed: number = 1;
33 |
34 | constructor(fragShader: string, target: HTMLElement) {
35 | this.target = target;
36 |
37 | if (this.target.tagName === "CANVAS") {
38 | this.canvas = this.target as HTMLCanvasElement;
39 | } else {
40 | this.canvas = document.createElement("canvas");
41 | target.append(this.canvas);
42 |
43 | // Setting this in body would break the fullscreen look
44 | if (target !== document.body) {
45 | const style = getComputedStyle(target);
46 | const radius = style.borderRadius || 0;
47 | target.style.clipPath = `inset(0 0 0 0 round ${radius})`;
48 | }
49 |
50 | this.canvas.style.position = "fixed";
51 | this.canvas.style.left = "0%";
52 | this.canvas.style.right = "0%";
53 | this.canvas.style.top = "0%";
54 | this.canvas.style.bottom = "0%";
55 | this.canvas.style.width = "100%";
56 | this.canvas.style.height = "100%";
57 | }
58 |
59 | // Fetches the attributes and sets quality and speed if given
60 | this.updateAttributes();
61 |
62 | // Get gl context from canvas
63 | this.gl = (this.canvas.getContext("webgl") || this.canvas.getContext("experimental-webgl")) as WebGLRenderingContext;
64 | this.loadShader(fragShader);
65 |
66 | // Create buffer
67 | let array = new Float32Array([-1, 3, -1, -1, 3, -1]);
68 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.gl.createBuffer());
69 | this.gl.bufferData(this.gl.ARRAY_BUFFER, array, this.gl.STATIC_DRAW);
70 |
71 | let al = this.gl.getAttribLocation(this.pid, "coords");
72 | this.gl.vertexAttribPointer(al, 2, this.gl.FLOAT, false, 0, 0);
73 | this.gl.enableVertexAttribArray(al);
74 |
75 | window.addEventListener("resize", this.eventHandlerResize);
76 | window.addEventListener("mousemove", this.eventHandlerMouse);
77 |
78 | const observer = new MutationObserver((_) => {
79 | this.updateAttributes();
80 | });
81 |
82 | observer.observe(target, {
83 | subtree: false,
84 | childList: false,
85 | attributes: true,
86 | attributeFilter: ["data-shader", "data-shader-quality", "data-shader-speed"],
87 | });
88 |
89 | this.update();
90 | }
91 |
92 | public loadShader(fragShader: string) {
93 | // Before initializing new shader, remove old ones if present
94 | if (this.pid) this.gl.deleteProgram(this.pid);
95 | if (this.vert) this.gl.deleteShader(this.vert);
96 | if (this.frag) this.gl.deleteShader(this.frag);
97 |
98 | // Create shader program
99 | this.pid = this.gl.createProgram();
100 |
101 | // Compile vertex shader
102 | this.vert = createShader(this.gl, vertShader, this.gl.VERTEX_SHADER);
103 | this.gl.attachShader(this.pid, this.vert);
104 |
105 | // Compile fragment shader
106 | this.frag = createShader(this.gl, fragShader, this.gl.FRAGMENT_SHADER);
107 | this.gl.attachShader(this.pid, this.frag);
108 | this.gl.linkProgram(this.pid);
109 | this.gl.useProgram(this.pid);
110 |
111 | // Read uniforms
112 | this.uniformResolution = this.gl.getUniformLocation(this.pid, "resolution");
113 | this.uniformTime = this.gl.getUniformLocation(this.pid, "time");
114 | this.uniformMouse = this.gl.getUniformLocation(this.pid, "mouse");
115 | }
116 |
117 | private updateAttributes() {
118 | this.quality = Number(this.target.attributes.getNamedItem("data-shader-quality")?.value) || this.quality;
119 | this.speed = Number(this.target.attributes.getNamedItem("data-shader-speed")?.value) || this.speed;
120 | this.canvas.style.zIndex = this.target.attributes.getNamedItem("data-shader-z-index")?.value || "-1";
121 |
122 | if (!this.target.attributes.getNamedItem("data-shader")?.value) {
123 | this.destroy();
124 | }
125 | }
126 |
127 | private resize() {
128 | if (this.target.tagName !== "CANVAS") {
129 | this.canvas.width = window.innerWidth / this.quality;
130 | this.canvas.height = window.innerHeight / this.quality;
131 | }
132 | }
133 |
134 | private mouse(e: MouseEvent) {
135 | this.mouseX = e.clientX;
136 | this.mouseY = e.clientY;
137 | }
138 |
139 | private update() {
140 | const {width, height} = this.canvas;
141 |
142 | if (!this.running)
143 | return;
144 |
145 | this.gl.uniform2f(this.uniformMouse, this.mouseX / width / this.quality, 1 - this.mouseY / height / this.quality);
146 | this.gl.uniform2f(this.uniformResolution, width, height);
147 | this.gl.uniform1f(this.uniformTime, performance.now() / 1000 * this.speed);
148 |
149 | this.gl.viewport(0, 0, width, height);
150 | this.gl.clearColor(0, 0, 0, 0);
151 | this.gl.clear(this.gl.COLOR_BUFFER_BIT);
152 | this.gl.drawArrays(this.gl.TRIANGLES, 0, 3);
153 |
154 | requestAnimationFrame(this.update.bind(this));
155 | }
156 |
157 | /**
158 | * Returns a WebGLUniformLocation for the given name in the fragment shader.
159 | * @param {string} name
160 | * @returns {WebGLUniformLocation}
161 | */
162 | public getUniform(name: string): WebGLUniformLocation {
163 | return this.gl.getUniformLocation(this.pid, name);
164 | }
165 |
166 | /**
167 | * This function has to be called to clean up the WebGL context.
168 | * It is also called when the element is removed from the DOM.
169 | * @returns {void}
170 | */
171 | public destroy(): void {
172 | (this.target as any).shadify = undefined;
173 |
174 | this.running = false;
175 |
176 | this.gl.deleteProgram(this.pid);
177 | this.gl.deleteShader(this.vert);
178 | this.gl.deleteShader(this.frag);
179 | this.canvas.remove();
180 |
181 | this.pid = null;
182 | this.vert = null;
183 | this.frag = null;
184 |
185 | window.removeEventListener("resize", this.eventHandlerResize);
186 | window.removeEventListener("mousemove", this.eventHandlerMouse);
187 | }
188 |
189 | get quality(): number {
190 | return this._quality;
191 | }
192 |
193 | set quality(value: number) {
194 | this._quality = value;
195 | this.resize();
196 | }
197 | }
198 |
199 | /**
200 | * Helper function for creating a WebGLShader in Wallpaper class
201 | * @param {WebGLRenderingContext} gl
202 | * @param {string} sourceCode
203 | * @param {number} type
204 | * @returns {WebGLShader}
205 | */
206 | function createShader(gl: WebGLRenderingContext, sourceCode: string, type: number): WebGLShader {
207 | const shader = gl.createShader(type);
208 | gl.shaderSource(shader, sourceCode);
209 | gl.compileShader(shader);
210 |
211 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
212 | const info = gl.getShaderInfoLog(shader);
213 | throw `Could not compile WebGL program. \n\n${info}`;
214 | }
215 | return shader;
216 | }
217 |
218 | /**
219 | * Fetches the shader code from the given URL.
220 | * If URL is from glslsandbox.com, it will fetch the code from the item page.
221 | * @param {string} url
222 | * @returns {Promise}
223 | */
224 | async function getCodeFromURL(url: string): Promise {
225 | // link to glslsandbox website is a special case
226 | if (url.includes("glslsandbox.com") && url.includes("/e#")) {
227 | url = url.replace("/e#", "/item/");
228 | const res = await fetch(url);
229 | const text = await res.json();
230 |
231 | if (typeof text.code === "undefined")
232 | throw new Error("Could not get shader code");
233 |
234 | return text.code;
235 | }
236 |
237 | // otherwise we expect a link to a source file and return the code
238 | const res = await fetch(url);
239 | return await res.text();
240 | }
241 |
242 | /**
243 | * Helper function for handling elements with the "data-shader" attribute.
244 | * @param {HTMLElement} target
245 | * @returns {void}
246 | */
247 | async function handleHTMLElement(target: HTMLElement): Promise {
248 | if (target == null)
249 | return;
250 |
251 | const url = target.attributes.getNamedItem("data-shader")?.value;
252 |
253 | if (url == null || url.trim().length == 0)
254 | return;
255 |
256 | if ((target as any)?.shadify != null) {
257 | if ((target as any).shadify.target == target)
258 | (target as any).shadify.loadShader(await getCodeFromURL(url));
259 | else
260 | (target as any).shadify.destroy();
261 | } else {
262 | console.log(target);
263 | (target as any).shadify = new Wallpaper(await getCodeFromURL(url), target);
264 | }
265 | }
266 |
267 | /**
268 | * Initializes Shadify.
269 | * @returns {void}
270 | */
271 | function init(): void {
272 | const observer = new MutationObserver((mutationList) => {
273 | console.log(mutationList, mutationList
274 | .filter(x => mutationList.filter(y => y.target == x.target).length >= mutationList.length-1));
275 |
276 | mutationList
277 | .filter(x => mutationList.filter(y => y.target == x.target).length >= mutationList.length-1) // filter duplicate targets
278 | .forEach((mutation) => handleHTMLElement(mutation.target as HTMLElement));
279 | });
280 |
281 | observer.observe(document.body, {
282 | subtree: true,
283 | childList: false,
284 | attributeFilter: ["data-shader"]
285 | });
286 |
287 | // Handle elements with the "data-shader" attribute
288 | document.querySelectorAll('[data-shader]').forEach((e) => handleHTMLElement(e as HTMLElement));
289 |
290 | document.removeEventListener("DOMContentLoaded", init);
291 | }
292 |
293 | document.addEventListener("DOMContentLoaded", init);
294 | }
295 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Shadify } from "./Shadify";
2 | export default Shadify;
3 | Object.assign(module.exports, Shadify);
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist/",
4 | "noImplicitAny": true,
5 | "declaration": true,
6 | "module": "commonjs",
7 | "target": "es2015",
8 | "allowJs": true,
9 | "moduleResolution": "node"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var libraryName = 'Shadify';
2 | var outputFile = libraryName + '.js';
3 |
4 | module.exports = {
5 | entry: __dirname + '/src/index.ts',
6 | devtool: 'source-map',
7 | output: {
8 | path: __dirname + '/lib',
9 | filename: outputFile,
10 | library: libraryName,
11 | libraryTarget: "var"
12 | },
13 | module: {
14 | rules: [
15 | {
16 | test: /\.tsx?$/,
17 | use: 'ts-loader',
18 | exclude: /node_modules/,
19 | },
20 | ],
21 | },
22 | resolve: {
23 | extensions: ['.tsx', '.ts', '.js'],
24 | },
25 | };
26 |
--------------------------------------------------------------------------------