{"id":13805683,"url":"https://github.com/martinlaxenaire/curtainsjs","last_synced_at":"2025-05-14T18:04:53.193Z","repository":{"id":38899017,"uuid":"131020675","full_name":"martinlaxenaire/curtainsjs","owner":"martinlaxenaire","description":"curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes.","archived":false,"fork":false,"pushed_at":"2025-04-03T08:29:16.000Z","size":45752,"stargazers_count":1732,"open_issues_count":13,"forks_count":114,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-05-04T12:01:47.470Z","etag":null,"topics":["3d","canvas","curtains","curtainsjs","dom","glsl","html5","javascript","responsive","shader","shaders","texture","webgl","webgl2"],"latest_commit_sha":null,"homepage":"https://www.curtainsjs.com/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martinlaxenaire.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["martinlaxenaire"],"custom":["https://www.paypal.me/martinlaxenaire"]}},"created_at":"2018-04-25T14:40:13.000Z","updated_at":"2025-05-03T20:21:07.000Z","dependencies_parsed_at":"2023-02-06T12:30:54.121Z","dependency_job_id":"440c4dc4-011c-4ae1-9742-1cfeae00dbce","html_url":"https://github.com/martinlaxenaire/curtainsjs","commit_stats":{"total_commits":298,"total_committers":9,"mean_commits":"33.111111111111114","dds":"0.046979865771812124","last_synced_commit":"07ec020af9d71dc653bb5a3f138c2ce5169cd818"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinlaxenaire%2Fcurtainsjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinlaxenaire%2Fcurtainsjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinlaxenaire%2Fcurtainsjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martinlaxenaire%2Fcurtainsjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martinlaxenaire","download_url":"https://codeload.github.com/martinlaxenaire/curtainsjs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254198514,"owners_count":22030965,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","canvas","curtains","curtainsjs","dom","glsl","html5","javascript","responsive","shader","shaders","texture","webgl","webgl2"],"created_at":"2024-08-04T01:01:03.817Z","updated_at":"2025-05-14T18:04:48.183Z","avatar_url":"https://github.com/martinlaxenaire.png","language":"JavaScript","funding_links":["https://github.com/sponsors/martinlaxenaire","https://www.paypal.me/martinlaxenaire"],"categories":["JavaScript","HTML","Canvas"],"sub_categories":[],"readme":"\u003ch2\u003eWhat is it ?\u003c/h2\u003e\n\u003cp\u003e\n    Shaders are the new front-end web developpment big thing, with the ability to create very powerful 3D interactions and animations. A lot of very good javascript libraries already handle WebGL but with most of them it's kind of a headache to position your meshes relative to the DOM elements of your web page.\n\u003c/p\u003e\n\u003cp\u003e\n    curtains.js was created with just that issue in mind. It is a small vanilla WebGL javascript library that converts HTML elements containing images and videos into 3D WebGL textured planes, allowing you to animate them via shaders.\u003cbr /\u003e\n    You can define each plane size and position via CSS, which makes it super easy to add WebGL responsive planes all over your pages.\n\u003c/p\u003e\n\u003cp style=\"text-align: center;\"\u003e\n    \u003cimg src=\"https://github.com/martinlaxenaire/curtainsjs/blob/master/documentation/images/promo.gif\" alt=\"curtains.js demo gif\" width=\"300\" height=\"225\" /\u003e\n\u003c/p\u003e\n\u003ch2\u003eKnowledge and technical requirements\u003c/h2\u003e\n\u003cp\u003e\n    It is easy to use but you will of course have to possess good basics of HTML, CSS and javascript.\n\u003c/p\u003e\n\u003cp\u003e\n    If you've never heard about shaders, you may want to learn a bit more about them on \u003ca href=\"https://thebookofshaders.com/\" title=\"The Book of Shaders\" \u003eThe Book of Shaders\u003c/a\u003e for example. You will have to understand what are the vertex and fragment shaders, the use of uniforms as well as the GLSL syntax basics.\n\u003c/p\u003e\n\u003ch2\u003eInstallation and usage\u003c/h2\u003e\n\u003cdiv\u003e\n    You can directly download the files and start using the ES6 modules:\n    \n```javascript\nimport {Curtains, Plane} from 'path/to/src/index.mjs';\n\nconst curtains = new Curtains({\n    container: \"canvas\"\n});\n\nconst plane = new Plane(curtains, document.querySelector(\"#plane\"));\n```\n\u003c/div\u003e\n\u003cdiv\u003e\n    Or you can use npm:\n\n```\nnpm i curtainsjs\n```\n\n\u003c/div\u003e\n\u003cdiv\u003e\n    Load ES6 modules:\n\n```javascript\nimport {Curtains, Plane} from 'curtainsjs';\n```\n\n\u003c/div\u003e\n\u003cdiv\u003e\nIn a browser, you can use the UMD files located in the `dist` directory:\n    \n```html\n\u003cscript src=\"dist/curtains.umd.min.js\"\u003e\u003c/script\u003e\n```\n\n```javascript\nconst curtains = new Curtains({\n    container: \"canvas\"\n});\n\nconst plane = new Plane(curtains, document.querySelector(\"#plane\"));\n\n// etc\n```\n\n\u003c/div\u003e\n\n\u003ch3\u003eUsage with React\u003c/h3\u003e\n\nNote that if you are using React, you might want to try \u003ca href=\"https://github.com/martinlaxenaire/react-curtains\"\u003ereact-curtains\u003c/a\u003e, curtains.js official React package.\n\n\u003ch2\u003eDocumentation\u003c/h2\u003e\n\nThe library is split into classes modules. Most of them are used internally by the library but there are however a few classes meant to be used directly, exported in the [src/index.mjs](src/index.mjs) file.\n\n\u003ch3\u003eCore\u003c/h3\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\n        Curtains: appends a canvas to your container and instanciates the WebGL context. Also handles a few helpers like scroll and resize events, request animation frame loop, etc.\n    \u003c/li\u003e\n    \u003cli\u003e\n        Plane: creates a new Plane object bound to a HTML element.\n    \u003c/li\u003e\n    \u003cli\u003e\n        Textures: creates a new Texture object.\n    \u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch3\u003eFrame Buffer Objects\u003c/h3\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\n        RenderTarget: creates a frame buffer object.\n    \u003c/li\u003e\n    \u003cli\u003e\n        ShaderPass: creates a post processing pass using a RenderTarget object.\n    \u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch3\u003eLoader\u003c/h3\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\n        TextureLoader: loads HTML media elements such as images, videos or canvases and creates Texture objects using those sources.\n    \u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch3\u003eMath\u003c/h3\u003e\n\n\u003cul\u003e\n    \u003cli\u003e\n        Vec2: creates a new Vector 2.\n    \u003c/li\u003e\n    \u003cli\u003e\n        Vec3: creates a new Vector 3.\n    \u003c/li\u003e\n    \u003cli\u003e\n        Mat4: creates a new Matrix 4.\n    \u003c/li\u003e\n    \u003cli\u003e\n        Quat: creates a new Quaternion.\n    \u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch3\u003eExtras\u003c/h3\u003e\n\n\u003cul\u003e\n\u003cli\u003e\n        PingPongPlane: creates a plane that uses FBOs ping pong to read/write a texture.\n    \u003c/li\u003e\n    \u003cli\u003e\n        FXAAPass: creates an antialiasing FXAA pass using a ShaderPass object.\n    \u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch3\u003eFull documentation\u003c/h3\u003e\n\n\u003cp\u003e\n    \u003ca href=\"https://www.curtainsjs.com/get-started.html\" title=\"Getting started\" target=\"_blank\"\u003eGetting started\u003c/a\u003e\u003cbr /\u003e\n    \u003ca href=\"https://www.curtainsjs.com/documentation.html\" title=\"API docs\" target=\"_blank\"\u003eAPI docs\u003c/a\u003e\u003cbr /\u003e\n    \u003ca href=\"https://www.curtainsjs.com/index.html#examples\"\u003eExamples\u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch2\u003eBasic example\u003c/h2\u003e\n\n\u003ch3\u003eHTML\u003c/h3\u003e\n\n```html\n\u003cbody\u003e\n    \u003c!-- div that will hold our WebGL canvas --\u003e\n    \u003cdiv id=\"canvas\"\u003e\u003c/div\u003e\n    \n    \u003c!-- div used to create our plane --\u003e\n    \u003cdiv class=\"plane\"\u003e\n    \n        \u003c!-- image that will be used as texture by our plane --\u003e\n        \u003cimg src=\"path/to/my-image.jpg\" crossorigin=\"\" /\u003e\n    \u003c/div\u003e\n    \n\u003c/body\u003e\n```\n\n\u003ch3\u003eCSS\u003c/h3\u003e\n\n```css\nbody {\n    /* make the body fits our viewport */\n    position: relative;\n    width: 100%;\n    height: 100vh;\n    margin: 0;\n    overflow: hidden;\n}\n\n#canvas {\n    /* make the canvas wrapper fits the document */\n    position: absolute;\n    top: 0;\n    right: 0;\n    bottom: 0;\n    left: 0;\n}\n\n.plane {\n    /* define the size of your plane */\n    width: 80%;\n    height: 80vh;\n    margin: 10vh auto;\n}\n\n.plane img {\n    /* hide the img element */\n    display: none;\n}\n```\n\n\u003ch3\u003eJavascript\u003c/h3\u003e\n\n```javascript\nimport {Curtains, Plane} from 'curtainsjs';\n\nwindow.addEventListener(\"load\", () =\u003e {\n    // set up our WebGL context and append the canvas to our wrapper\n    const curtains = new Curtains({\n        container: \"canvas\"\n    });\n    \n    // get our plane element\n    const planeElement = document.getElementsByClassName(\"plane\")[0];\n    \n    // set our initial parameters (basic uniforms)\n    const params = {\n        vertexShaderID: \"plane-vs\", // our vertex shader ID\n        fragmentShaderID: \"plane-fs\", // our fragment shader ID\n        uniforms: {\n            time: {\n                name: \"uTime\", // uniform name that will be passed to our shaders\n                type: \"1f\", // this means our uniform is a float\n                value: 0,\n            },\n        },\n    };\n    \n    // create our plane using our curtains object, the bound HTML element and the parameters\n    const plane = new Plane(curtains, planeElement, params);\n    \n    plane.onRender(() =\u003e {\n        // use the onRender method of our plane fired at each requestAnimationFrame call\n        plane.uniforms.time.value++; // update our time uniform value\n    });\n    \n});\n```\n\n\u003ch3\u003eShaders\u003c/h3\u003e\n\n\u003ch4\u003eVertex shader\u003c/h4\u003e\n\n```glsl\n\u003cscript id=\"plane-vs\" type=\"x-shader/x-vertex\"\u003e\n    #ifdef GL_ES\n    precision mediump float;\n    #endif\n    \n    // those are the mandatory attributes that the lib sets\n    attribute vec3 aVertexPosition;\n    attribute vec2 aTextureCoord;\n    \n    // those are mandatory uniforms that the lib sets and that contain our model view and projection matrix\n    uniform mat4 uMVMatrix;\n    uniform mat4 uPMatrix;\n    \n    // our texture matrix that will handle image cover\n    uniform mat4 uTextureMatrix0;\n    \n    // pass your vertex and texture coords to the fragment shader\n    varying vec3 vVertexPosition;\n    varying vec2 vTextureCoord;\n    \n    void main() {       \n        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n        \n        // set the varyings\n        // here we use our texture matrix to calculate the accurate texture coords\n        vTextureCoord = (uTextureMatrix0 * vec4(aTextureCoord, 0.0, 1.0)).xy;\n        vVertexPosition = aVertexPosition;\n    }\n\u003c/script\u003e \n```\n\n\u003ch4\u003eFragment shader\u003c/h4\u003e\n\n```glsl\n\u003cscript id=\"plane-fs\" type=\"x-shader/x-fragment\"\u003e\n    #ifdef GL_ES\n    precision mediump float;\n    #endif\n    \n    // get our varyings\n    varying vec3 vVertexPosition;\n    varying vec2 vTextureCoord;\n    \n    // the uniform we declared inside our javascript\n    uniform float uTime;\n    \n    // our texture sampler (default name, to use a different name please refer to the documentation)\n    uniform sampler2D uSampler0;\n    \n    void main() {\n        // get our texture coords from our varying\n        vec2 textureCoord = vTextureCoord;\n        \n        // displace our pixels along the X axis based on our time uniform\n        // textures coords are ranging from 0.0 to 1.0 on both axis\n        textureCoord.x += sin(textureCoord.y * 25.0) * cos(textureCoord.x * 25.0) * (cos(uTime / 50.0)) / 25.0;\n        \n        // map our texture with the texture matrix coords\n        gl_FragColor = texture2D(uSampler0, textureCoord);\n    }\n\u003c/script\u003e \n```\n\n\u003ch2\u003eChangelog\u003c/h2\u003e\n\nComplete [changelog](CHANGELOG.md) starting from version 7.1.0","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinlaxenaire%2Fcurtainsjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartinlaxenaire%2Fcurtainsjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartinlaxenaire%2Fcurtainsjs/lists"}