• Stars
    star
    843
  • Rank 54,052 (Top 2 %)
  • Language
    JavaScript
  • Created over 3 years ago
  • Updated over 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

๐Ÿงžโ€โ™‚๏ธ Your magic WebGL carpet

โš ๏ธโš ๏ธ BETA! โš ๏ธโš ๏ธ (Most likely I won't maintain this...)

๐Ÿงžโ€โ™‚๏ธ Aladino โ€“ย your magic WebGL carpet

Aladino is a tiny (around ~5kb gzipped) and dependency-free javascript library that allows to enhance your site using "shader effects".
The library is using WebGL1 and has progressive enhancement and accessibility in mind.

Because examples are worth thousand of words: https://luruke.github.io/aladino/

It was developed during some R&D time at EPIC Agency, back in 2019, and it's currently used on:

๐Ÿคทโ€ Even why?

CSS is cool and powerful, you can build complex responsive layouts and more. Unfortunately the creative interactions you can achieve are very limited (only basic transforms, basic set of CSS filters).

Following the footsteps of an old and depracated CSS spec (Custom filters aka CSS Shaders) this library allow to "augment" your DOM elements.

๐Ÿ”Ž How it even works?

Aladino operates on a full-screen canvas as position: fixed. You'll likely want this canvas to be as background or foreground of your site. When any Element is added to aladino, the original DOM element will be hidden (via opacity: 0), and a WebGL plane with the exact same size and position will be created in the canvas.

At resize and page scroll, aladino will make sure the WebGL plane matches the position and size of your DOM element.

Instead of using an orthographic camera, a perspective one is used, so you can make fancy perspective effects easily.

๐Ÿ“ Tailor-made rendering

The library itself is tiny and doesn't support many of the WebGL capabilities (stencil, cube maps, array uniforms...). The rendering approach is very tailored for this specific use case.

If you need to render 3D objects and do more complex things, you might want to use libraries like pixi, ogl or three.js and build the dom->gl layer yourself.

Some features:

  • Reduce WebGL state change to the strict minimum (try to, without getting too complex, it can be greatly improved).
  • Use VAO (via OES_vertex_array_object).
  • Use a single geometry (a plane) for all the draw calls.
  • Automatically cache textures via the URL.
  • Uses ResizeObserver where available to track Element size.
  • Postprocessing using a big-triangle technique.

๐ŸŒŽ Browser support

The library should work on every browser supporting WebGL, eventually you might want to add a polyfill for OES_vertex_array_object.
For older browsers, you might need to transpile the code in order to support ES features like class, Map, destructuring.

๐Ÿ“ How to use it

The library has three main concepts:

  • Aladino, the main instance, you'll generally have only one in your app.
  • Carpet, the representation of your DOM element on the canvas (a plane mesh).
  • Material, the WebGL program (vertex + fragment shader) that will be used by your carpets.

A very basic example:

import Aladino from "aladino";

const aladino = new Aladino();
document.body.appendChild(aladino.canvas);

aladino.carpet(document.querySelector(".element"), {
  material: aladino.material({
    vertex: `
      attribute vec2 position;
      attribute vec2 uv;

      uniform mat4 projection;
      uniform float time;

      void main() {
        vec4 p = vec4(position, 0.0, 1.0);
        p.z += sin(uv.x * 3.0 + time * 0.003);

        gl_Position = projection * p;
      }
    `,
    fragment: `
      precision highp float;

      void main() {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
      }
    `,
  }),
});

Running this piece of code, will create a green animating box, that replace your .element.

๐Ÿ API

Arguments with the default values and methods.


Aladino

const aladino = new Aladino({
  // HTMLCanvasElement โ€“ The canvas to use
  canvas: document.createElement("canvas"),

  // Number โ€“ Pixel ratio to use (example for retina displays)
  dpr: Math.min(devicePixelRatio, 2),

  // Number โ€“ Define horizontal and vertical mesh density of the plane
  density: 1,

  // Boolean โ€“ Whether need to track the page scroll (`scroll` on `window`)
  autoScroll: true,

  // WebGLContextAttributes โ€“ An object for WebGL context attributes
  attribs: { antialias: true },

  // Object { fragment: String, uniforms: Object } - Enable postprocessing using a big-triangle
  post: false,
});

// WebGLRenderingContext - The WebGL context used by aladino
aladino.gl;

// Number โ€“ Read/set the horizontal scrolling in px
aladino.x;

// Number โ€“ Read/set the vertical scrolling in px
aladino.y;

// Map - All carpets instances
aladino.carpets;

// Carpet - Get the carpet instance of a specific Element
aladino.carpets.get(document.querySelector(".element"));

// Force the resize
aladino.resize();

// Destroy the instance
aladino.destroy();

Material

const material = aladino.material({
  // String โ€“ Vertex shader
  vertex: `
    attribute vec2 position;
    attribute vec2 uv;

    uniform mat4 projection;
    uniform vec2 size;
    uniform float time;

    void main() {
      vec4 p = vec4(position, 0.0, 1.0);
      gl_Position = projection * p;
    }
  `,

  // String โ€“ Fragment shader
  fragment: `
    precision highp float;

    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `,

  // Object - Uniforms shared across all the carpets using this material
  uniforms: {},
  /*
    {
      enable: false,         // uniform bool enable;
      speed: 0.4,            // uniform float speed;
      scale: [1, 1.2],       // uniform vec2 scale;
      color: [1, 0, 0],      // uniform vec3 color;
      color2: [1, 0, 0, 1],  // uniform vec4 color2;
      tex: aladino.texture() // uniform sampler2D tex;
    }
  */
});

Default attributes / uniforms:

attribute vec2 position;
attribute vec2 uv;

uniform mat4 projection;
uniform vec2 size; // Size in px of your carpet
uniform vec2 viewport;
uniform float time; // current time

// When using a `sampler2D` texture, it will automatically send the size in px of the texture
uniform vec2 size$SAMPLER_UNIFORM_NAME;

Carpet

const carpet = aladino.carpet(Element, {
  // Aladino Material โ€“ The material to use for this carpet
  material,

  // Boolean โ€“ Use gl.LINES
  wireframe: false,

  // Array[Number, Number] โ€“ย Offset in px [x, y]
  position: [0, 0],

  // Array[Number, Number] โ€“ย Scale multiplier [width, height]
  scale: [1, 1],

  // Number - order of depth of the carpet in the scene
  order: 10,

  // Uniforms โ€“ Uniforms specific to this carpet
  uniforms: {},
});

// Force the resize of the carpet
// Note, aladino uses `ResizeObserver` API, so already tracks some element changes
carpet.resize();

// Destroy the carpet
carpet.destroy();

// Boolean โ€“ย Indicate if the carpet is active, so if needs to be drawn an updated
carpet.active;

After creating a carpet, the DOM Element will be set as opacity: 0 and a CSS class aladino will be added.


Texture

// String โ€“ URL of the texture
const texture = aladino.texture(url, {
  // Boolean - Apply anisotropy texture filtering via `EXT_texture_filter_anisotropic`
  anisotropy: false,
});

// Promise<Texture>
texture.loading;

Texture instances can be passed as regular uniforms.


๐ŸŽ Tips 4 Performance

If you encour in performance issues:

  • Try to lower dpr.
  • Try to lower geometry density.
  • Try to disable antialias. (Expecially on retina displays, you might want to look into use GL_OES_standard_derivatives instead)
  • Try to share the same Material as much as possible on your carpets.
  • When possible, share uniforms per program then per single carpet.
  • Prefer vertex calculation then per pixel, exploiting the linear interpolation given by varyings variables.

Usually the biggest "price to pay" for this technique is the compositing phase between the canvas and the DOM due how browsers works internally.

๐Ÿค” Things to consider for Dev / Design

  • The library automatically tracks your scroll and keep in sync the WebGL, but in case of frame dropping during scroll, you will notice the DOM to be "smoother", that's because the browser prioritise it's own rendering instead of Javascript tasks - consider to use a "virtual scrolling" if needed.

  • If you're using a custom font, makes sure to "resize" aladino after the font is loaded (as it might cause a layout shifting).

  • The library tracks the position of your DOM elements at page load and resize and with ResizeObserver API if available. If you change the position of your item, you'll have to remember to use .resize() method.

๐Ÿ”จ TODO

  • Support video texture?
  • Add a method to verify WebGL support?
  • Test canvas position fixed vs transform: translate()?
  • Culling?
  • Blending modes?
  • Support text?
  • Create components for react / vue.js?
  • See if magicshader can be implemented easily?

More Repositories

1

browser-2020

Things you can do with a browser in 2020 โ˜•๏ธ
8,066
star
2

awesome-casestudy

๐Ÿ“• Curated list of technical case studies on WebGL and creative development
2,556
star
3

magicshader

๐Ÿ”ฎ Tiny helper for three.js to debug and write shaders
JavaScript
239
star
4

human-gpu

๐Ÿค– Hello human, I'm sick to be your GPU!!
JavaScript
177
star
5

antipasto

๐Ÿฝ Juicy starter for three.js
JavaScript
162
star
6

bidello

๐Ÿ‘จโ€๐Ÿซ Don't leave your Class alone
JavaScript
56
star
7

clip-rect

โœ‚๏ธ - clip-rect is an helper to create painless `clip: rect()` animations with GSAP.
JavaScript
42
star
8

gl-backend

๐Ÿšช POC Detect WebGL rendering backend
JavaScript
33
star
9

adapttext.js

๐Ÿ™Œ - AdaptText.js is a dependency free and simple javascript solution capable to fit your text inside the parent element
JavaScript
28
star
10

corrente

evening jelly experiment in WebGL
JavaScript
22
star
11

split-in-lines

Take an HTML element with text, and split it in lines.
JavaScript
18
star
12

tubbbo

ใ€ฐA little WebGL experiment
JavaScript
17
star
13

appear-animation

Easily add animation when elements comes in the viewport.
JavaScript
11
star
14

dev-excuses-chrome

Chrome, please, gimme an excuse! - A little chrome extension that allow you to easily fetch an excuse from https://api.githunt.io/programmingexcuses and paste it in your current textarea/input field.
JavaScript
9
star
15

monolith

ddd-2018 devx experiment
HTML
6
star
16

three-emoji

emh...emoji!!
HTML
4
star
17

selfielapse

Simple desktop app that take a snapshot a day from your webcam.
JavaScript
4
star
18

totodile

A wild DOMElement appears!
JavaScript
3
star
19

infinite-runner-code-golf

A little exercise in 510byte.
HTML
3
star
20

luruke.com

Personal site
HTML
2
star
21

fastnavigation

a proof of concept for a fast navigation PJAX+localstorage+intent
HTML
1
star
22

hasshhh

Generate number starting from a string seed
JavaScript
1
star
23

vooautoconnect

Keep the connection up using VOO_HOMESPOT wifi
Shell
1
star
24

foglio

HTML
1
star
25

test-babylon-export-gltf

HTML
1
star
26

intl-localematcher

JavaScript
1
star
27

arcoreval

JavaScript
1
star