├── old ├── README.md ├── doc │ └── hydra-blending-modes.md ├── hydra-nowrap.js └── hydra-blending-modes.js ├── doc ├── hydra-debug.md ├── hydra-vec4.md ├── hydra-swizzle.md ├── hydra-mouse.md ├── hydra-gif.md ├── hydra-fractals.md ├── hydra-wrap.md ├── hydra-tap.md ├── hydra-canvas.md ├── hydra-arrays.md ├── hydra-text.md ├── hydra-blend.md ├── hydra-outputs.md ├── hydra-arithmetics.md ├── ES │ └── hydra-glsl.md ├── hydra-glsl.md ├── hydra-pixels.md └── hydra-colorspaces.md ├── .github └── FUNDING.yml ├── hydra-pip.js ├── hydra-canvas.js ├── hydra-pixels.js ├── hydra-mouse.js ├── hydra-arrays.js ├── utils └── README.md ├── hydra-vec4.js ├── hydra-outputs.js ├── hydra-fractals.js ├── hydra-swizzle.js ├── hydra-gradientmap.js ├── hydra-tap.js ├── hydra-blend.js ├── hydra-src.js ├── hydra-text.js ├── hydra-debug.js ├── hydra-glsl.js ├── hydra-wrap.js ├── hydra-arithmetics.js ├── hydra-abbreviations.js ├── hydra-convolutions.js ├── README.md └── hydra-colorspaces.js /old/README.md: -------------------------------------------------------------------------------- 1 | # WARNING : deprecated 2 | 3 | Extensions in this folder are deprecated in favor of newer extensions. 4 | 5 | * hydra-blending-modes : use hydra-blend instead 6 | * hydra-nowrap : use hydra-wrap and set wrapping to nowrap instead -------------------------------------------------------------------------------- /doc/hydra-debug.md: -------------------------------------------------------------------------------- 1 | # hydra-debug 2 | 3 | Adds tools to make it easier to debug sketches. As of now there's only one tool, but it's a useful one! 4 | 5 | ## Show fragment shader on screen 6 | 7 | ### .debug() 8 | 9 | `osc().debug()` 10 | 11 | The `debug` function will open a pop up inside Hydra showing the resulting fragment shader of any hydra texture. 12 | 13 | `osc().debug(output)` 14 | 15 | If you pass an output to the debug function, the pop up will be editable and will also have a `>` (run) button, allowing you to edit the fragment shader and re-run it. Note that the highlighting won't update until you press the `>` button. -------------------------------------------------------------------------------- /doc/hydra-vec4.md: -------------------------------------------------------------------------------- 1 | # hydra-vec4 2 | 3 | Adds wrapper functions that allow you to construct vec4's like you would in GLSL. 4 | 5 | ## Overview 6 | 7 | This extension adds functions for `vec4`, `vec3` and `vec2`. `vec4` will return a `solid()` with the corresponding values, while `vec3` and `vec2` simply return arrays to be sent to the `vec4` function. However the extension takes into account if an array was created by a `vec`function, so you can still use arrays and functions as you would do normally in Hydra even as arguments to these new functions. 8 | 9 | ## Example 10 | 11 | ```js 12 | noise() 13 | .mult( vec4( vec3(0.5) , 1 ) ) 14 | .add( vec4( [0.5,0].smooth() ) ) 15 | .layer( 16 | vec4( vec3( [0, 1, 0.5] , vec2( ()=>time%1 ) ) , 1) 17 | .mask(shape(4)) 18 | ) 19 | .out() 20 | ``` -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: geikha 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /doc/hydra-swizzle.md: -------------------------------------------------------------------------------- 1 | # hydra-swizzle 2 | 3 | This extensions replicates the [swizzling functionality from GLSL](https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Swizzling) in Hydra. 4 | 5 | ## Example 6 | 7 | ```js 8 | gradient(1).rbg 9 | .layer(osc(30,.1,2).bggr) 10 | .layer(gradient().r.mask(shape(2))) 11 | .out() 12 | ``` 13 | 14 | ## Overview 15 | 16 | hydra-swizzle defines properties for each element in `rgba`and `xyzw`. As well for all possible combinations of `rgb`, `xyz`, `rgba` and `xyzw`. When you call only one element, It'll replicate itself to all channels except alpha. When you call 3 elements, It'll call them as expected and leave alpha as is. When you call 4 elements, they get assigned as you would expect. 17 | 18 | ```js 19 | texture().rgg // -> c0.rrga 20 | texture().bgba // -> c0.bgba 21 | texture().r // -> c0.rrra 22 | ``` 23 | -------------------------------------------------------------------------------- /doc/hydra-mouse.md: -------------------------------------------------------------------------------- 1 | # hydra-mouse 2 | 3 | This extension replaces the default mouse object used by Hydra, adding new properties. 4 | 5 | --- 6 | 7 | ## Properties 8 | 9 | ***Note:** In the next list, `mouse x y` means `mouse.x` and `mouse.y`* 10 | 11 | | properties | range / unit | relative to / absolute | 12 | |--------------------|------------------|------------------------| 13 | |`mouse x y` | pixels | browser's viewport | 14 | |`mouse ax ay` | pixels | absolute | 15 | |`mouse cx cy` | pixels | canvas (like p5) | 16 | |`mouse rx ry` | [0; 1] | browser's viewport | 17 | |`mouse crx cry` | [0; 1] | canvas | 18 | |`mouse posx posy` | [0.5; -0.5] | browser's viewport | 19 | |`mouse cposx cposy` | [0.5; -0.5] | canvas | 20 | 21 | ### Note 22 | 23 | You may wonder why is the range `[0.5; -0.5]` in an inverted order. This is because inside Hydra, scrolling by a positive number scrolls to the left or up, while scrolling by a negative one scrolls to the right or down. -------------------------------------------------------------------------------- /doc/hydra-gif.md: -------------------------------------------------------------------------------- 1 | # hydra-gif 2 | 3 | Load `.gif` files to Hydra sources. 4 | 5 | ## Overview 6 | 7 | This extension uses [this GIF to canvas script](https://stackoverflow.com/questions/48234696/how-to-put-a-gif-with-canvas). This method has to completely uncompress the GIF so try to keep your GIFs light. Each source gets their own `.gif` property which allows you to change how the gif plays. 8 | 9 | ## Usage 10 | 11 | ### .initGif() 12 | 13 | `s0.initGif(url, delay, params)` 14 | 15 | Initialize a running GIF in the given source. `delay` allows you to set a delay in milliseconds from frame to frame, however this argument is optional and you can change it later. `params` is an object with properties for the regl texture that will act as the source, as the same with any other `.init` method. 16 | 17 | ### s0.gif 18 | 19 | `s0.gif` 20 | 21 | This is the object representing the gif, here are some of its useful properties: 22 | 23 | * `s0.gif.delay` : change the delay in milliseconds from frame to frame 24 | * `s0.gif.playSpeed` : change the play speed overall, you can set to negative values to go on reverse 25 | * `s0.gif.width` 26 | * `s0.gif.height` -------------------------------------------------------------------------------- /old/doc/hydra-blending-modes.md: -------------------------------------------------------------------------------- 1 | # hydra-blending-modes 2 | 3 | ## **warning : deprecated !! use hydra-blend instead** 4 | 5 | Very simple but useful blending modes for Hydra. They all lack an intensity argument as of now. 6 | Taken from [hydra-blocky](https://github.com/samarthgulati/hydra-blockly/). 7 | Specifically [this file](https://github.com/samarthgulati/hydra-blockly/blob/master/image-editing-glsl-functions.js). 8 | 9 | See all available blending modes by evaluating: 10 | 11 | ```js 12 | > console.log(blendmodes_list) 13 | ``` 14 | 15 | ```js 16 | { 17 | 0: "darken", 18 | 1: "multiply", 19 | 2: "colorBurn", 20 | 3: "linearBurn", 21 | 4: "darkerColor", 22 | 5: "lighten", 23 | 6: "screen", 24 | 7: "colorDodge", 25 | 8: "linearDodge", 26 | 9: "lighterColor", 27 | 10: "overlay", 28 | 11: "softLight", 29 | 12: "hardLight", 30 | 13: "vividLight", 31 | 14: "linearLight", 32 | 15: "pinLight", 33 | 16: "hardMix", 34 | 17: "difference", 35 | 18: "exclusion", 36 | 19: "subtract", 37 | 20: "divide", 38 | 21: "hueBlend", 39 | 22: "colorBlend", 40 | 23: "saturationBlend", 41 | 24: "luminosityBlend" 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /hydra-pip.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const video = document.createElement("video"); video.autoplay = true; 31 | const canvas = _hydra.canvas; 32 | const stream = canvas.captureStream(); 33 | video.srcObject = stream; 34 | 35 | window.hydraPip = ()=> video.requestPictureInPicture(); 36 | window.hydraPictureInPicture = window.hydraPip; 37 | } 38 | -------------------------------------------------------------------------------- /doc/hydra-fractals.md: -------------------------------------------------------------------------------- 1 | # hydra-fractals 2 | 3 | Adds mirroring and other functions that can be useful for making fractals. You should also check [`hydra-wrap`](./hydra-wrap.md) for fractals. 4 | 5 | ## Mirroring functions 6 | 7 | All mirroring functions are named after the axis which will be affected. 8 | 9 | 10 | ### mirrorX 11 | 12 | `.mirrorX( pos = 0 , coverage = 1 )` 13 | 14 | Result: `[1234|4321]` 15 | 16 | Leaves the image as is, up until `pos`, where it starts mirroring it. `coverage` is how long until the effect repeats itself. For example: 17 | 18 | `.mirrorX(0,0.5)` 19 | 20 | Result: `[12|21|12|21]` 21 | 22 | ### mirrorY 23 | 24 | `.mirrorY( pos = 0 , coverage = 1 )` 25 | 26 | Same as `mirrorX` but on the other axis. 27 | 28 | ### mirrorX2 29 | 30 | `.mirrorX2( pos = 0 , coverage = 1 )` 31 | 32 | Result: `[8765|5678]` 33 | 34 | Same as `mirrorX` but mirrors from the other side. 35 | 36 | ### mirrorY2 37 | 38 | `.mirrorY2( pos = 0 , coverage = 1 )` 39 | 40 | Same as `mirrorY` but mirrors from the other side. 41 | 42 | ### mirrorWrap 43 | 44 | `.mirrorWrap()` 45 | 46 | Will mirror any out of bounds coordinates. For example: 47 | 48 | Input: `[-8,-7,-6,-5,-4,-3,-2,-1] [1,2,3,4,5,6,7,8] [9,10,11,12,13,14,15,16]` 49 | Result: `[87654321] [12345678] [87654321]` 50 | 51 | ## Other functions 52 | 53 | ### inversion 54 | 55 | `.inversion()` 56 | 57 | Applies a circular inversion. -------------------------------------------------------------------------------- /doc/hydra-wrap.md: -------------------------------------------------------------------------------- 1 | # hydra-wrap 2 | 3 | Allows you to change how Hydra wraps textures, and control the wrapping of generators. 4 | 5 | ## Overview 6 | 7 | This extensions works by changing all coordinate functions (such as `scrollX`, `scale`, etc) and setting them to not wrap at all. This change makes it so that generator functions (such as `osc`, `shape`, etc) don't wrap when you change their coordinates. As for textures (such as `s0`, `o0`), it lets you change the way they wrap (it may be `wrap`, `nowrap`, `mirror`). If you want a generator to wrap in the same way as textures do, you can simply write, for example, `osc().wrap()`. 8 | 9 | --- 10 | 11 | ## Functions 12 | 13 | You can access the different functions via the `hydraWrap` object. 14 | 15 | | Method | Description | 16 | |-------------------|----------------------------------------------------| 17 | | hydraWrap.setWrap() | Sets the wrapping on. This is the default. | 18 | | hydraWrap.setNoWrap() | Disables wrapping. This means textures will clamp. | 19 | | hydraWrap.setMirror() | Sets the wrapping to mirror. | 20 | | hydraWrap.setVoid() | Void does not actually change the wrapping method, but it means that **textures** won't render out of bounds coordinates instead of wrapping. You could use `hydra-src` with `hydraMask` instead. | 21 | 22 | As mentioned above, this extensions also adds a new coordinate function named `wrap` which will set any generator to wrap. See the following example: 23 | 24 | ```js 25 | shape().scale(.2).out(o0) 26 | shape().wrap().scale(.2).out(o1) 27 | render() 28 | ``` 29 | -------------------------------------------------------------------------------- /hydra-canvas.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | _hydraScope.canvas = window._hydra.canvas; 30 | 31 | _hydraScope.canvas.setLinear = function () { 32 | this.style.imageRendering = "auto"; 33 | }; 34 | _hydraScope.canvas.setNearest = function () { 35 | this.style.imageRendering = "pixelated"; 36 | }; 37 | _hydraScope.canvas.setFullscreen = function (full = true) { 38 | const set = full ? "100%" : ""; 39 | this.style.width = set; 40 | this.style.height = set; 41 | }; 42 | _hydraScope.canvas.setAlign = function (align = "right") { 43 | this.style.position = "relative"; 44 | this.parentElement.style["text-align"] = align; 45 | }; 46 | _hydraScope.canvas.setRelativeSize = function (ratio) { 47 | this.style.width = "" + width * ratio + "px"; 48 | this.style.height = "" + height * ratio + "px"; 49 | }; 50 | -------------------------------------------------------------------------------- /doc/hydra-tap.md: -------------------------------------------------------------------------------- 1 | # hydra-tap 2 | 3 | A tap bpm control for Hydra. Inspired by Resolume's own tap. 4 | 5 | ## Example 6 | 7 | ```js 8 | osc(30,.01,beats(1)).out() 9 | 10 | osc().rotate(beats_(2).curve(-3)).out() 11 | 12 | osc().scale(beats(1).curve(2).range(1,2)).out() 13 | 14 | // Ctrl + Space Bar for tapping 15 | // Ctrl + , (Comma) for re-sync 16 | ``` 17 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlkcmEtZXh0ZW5zaW9ucy5nbGl0Y2gubWUlMkZoeWRyYS10YXAuanMlMjIpJTBBJTBBb3NjKDMwJTJDLjAxJTJDYmVhdHMoMSkpLm91dCgpJTBBJTBBb3NjKCkucm90YXRlKGJlYXRzXygyKS5jdXJ2ZSgtMykpLm91dCgpJTBBJTBBb3NjKCkuc2NhbGUoYmVhdHMoMSkuY3VydmUoMikucmFuZ2UoMSUyQzIpKS5vdXQoKSUwQSUwQSUyRiUyRiUyMEN0cmwlMjAlMkIlMjBTcGFjZSUyMEJhciUyMGZvciUyMHRhcHBpbmclMEElMkYlMkYlMjBDdHJsJTIwJTJCJTIwJTJDJTIwKENvbW1hKSUyMGZvciUyMHJlLXN5bmM%3D) 18 | 19 | ## Usage 20 | 21 | ### Tap 22 | 23 | * `Ctrl + Space Bar` for tapping. 24 | * `Ctrl + , (Comma)` for re-sync (resets time to 0). 25 | 26 | ### Envelopes 27 | 28 | * `beats(n=1)`: A linear ramp from 1 down to 0 every `n` beats 29 | * `beats_(n=1)`: A linear ramp from 0 up to 1 every `n` beats 30 | * `beatsTri(n=1)`: Goes from 1 to 0 on `n` beats, and then back to 1 in the same time, creating a triangle wave. 31 | * `beatsTri_(n=1)`: Same as avobe but inverted. 32 | 33 | #### Curve 34 | 35 | You can seat the curve of a beat envelope by calling `.curve(q)` on it. Positive values will ease in and negative values will ease out. For example, `beats().curve(3)` would be cubic easing in. 36 | 37 | #### Range 38 | 39 | You can also set the range for an envelope or a curved envelope by calling `.range(min=0,max=1)` on it. For example: `osc().scale(beatsTri(2).curve(2).range(1,2))`. -------------------------------------------------------------------------------- /hydra-pixels.js: -------------------------------------------------------------------------------- 1 | //hydra-pixels 2 | //read pixels from each hydra output 3 | 4 | { 5 | const getHydra = function () { 6 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 7 | ? "editor" 8 | : window.atom?.packages 9 | ? "atom" 10 | : "idk"; 11 | if (whereami === "editor") { 12 | return window.hydraSynth; 13 | } 14 | if (whereami === "atom") { 15 | return global.atom.packages.loadedPackages["atom-hydra"] 16 | .mainModule.main.hydra; 17 | } 18 | let _h = [ 19 | window.hydraSynth, 20 | window._hydra, 21 | window.hydra, 22 | window.h, 23 | window.H, 24 | window.hy 25 | ].find(h => h?.regl); 26 | return _h; 27 | }; 28 | window._hydra = getHydra(); 29 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 30 | } 31 | 32 | { 33 | const oP = _hydra.o[0].constructor.prototype; 34 | 35 | const regl = _hydra.o[0].regl; 36 | regl.attributes.preserveDrawingBuffer = true; 37 | oP.read = function (x = 0, y = 0, w = 1, h = 1) { 38 | return regl.read({ 39 | framebuffer: this.fbos[this.pingPongIndex], 40 | x: x, 41 | y: y, 42 | width: w, 43 | height: h, 44 | }); 45 | }; 46 | oP.readAll = function () { 47 | const fbo = this.fbos[this.pingPongIndex]; 48 | return regl.read({ 49 | framebuffer: fbo, 50 | x: 0, 51 | y: 0, 52 | width: fbo.width, 53 | height: fbo.height, 54 | }); 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /doc/hydra-canvas.md: -------------------------------------------------------------------------------- 1 | # hydra-canvas 2 | 3 | This extension provides several methods for modifying the appearance and behavior of a canvas element in a web page. 4 | 5 | ## The canvas object 6 | 7 | canvas: A reference to the canvas element with a class of bg-black. This makes this extension only work within the hydra editor. 8 | 9 | ## Methods 10 | 11 | ### `setLinear()` 12 | 13 | Sets the imageRendering property of the canvas element to auto, which causes the browser to use a linear interpolation algorithm for scaling the image. 14 | 15 | ### `setNearest()` 16 | 17 | Sets the imageRendering property of the canvas element to pixelated, which causes the browser to use nearest neighbor interpolation for scaling the image. This results in a pixelated appearance when the canvas is scaled. 18 | 19 | ### `setFullscreen(full=true)` 20 | 21 | Sets the width and height of the canvas element to 100% if full is true, or an empty string if full is false. This causes the canvas to fill the available space in the parent element. 22 | 23 | ### `setAlign(align='right')` 24 | 25 | Sets the text-align property of the parent element of the canvas element to the specified value. The default value is 'right'. 26 | 27 | ### `setRelativeSize(ratio)` 28 | 29 | Sets the width and height of the canvas element to a specified ratio of the current width and height. 30 | 31 | ## Examples 32 | 33 | ```javascript 34 | // Set the canvas to fill the available space in the parent element 35 | canvas.setFullscreen(); 36 | 37 | // Set the canvas to use nearest neighbor interpolation for scaling 38 | canvas.setNearest(); 39 | 40 | // Set the canvas to be 50% of its current size 41 | canvas.setRelativeSize(0.5); 42 | 43 | // Set the parent element of the canvas to align its contents to the left 44 | canvas.setAlign('left'); 45 | ``` 46 | -------------------------------------------------------------------------------- /doc/hydra-arrays.md: -------------------------------------------------------------------------------- 1 | # hydra-arrays 2 | 3 | Extends the functionality of arrays in Hydra, letting you operate between different arrays and generate new ones. 4 | 5 | ## Operators 6 | 7 | ### Regular operators 8 | 9 | `.add`, `.sub`, `.mult`, `.div`, `.mod` 10 | 11 | These will let you add either a constant or an array to another array. If the second array is smaller than the first one, it simply stops there. For example: 12 | 13 | ``` 14 | [0,1,2,3].add(1) == [1,2,3,4] 15 | [0,1,2,3].add([1,2]) == [1,3,2,3] 16 | ``` 17 | 18 | ### Wrap operators 19 | 20 | `.addWrap`, `.subWrap`, `.multWrap`, `.divWrap`, `.modWrap` 21 | 22 | These are just like the regular operators, but if the second array is smaller than the first one, it'll repeat itself until going through every single element in the first array. 23 | 24 | ``` 25 | [0,1,2,3].add([1,2]) == [1,3,2,3] 26 | [0,1,2,3].addWrap([1,2]) == [1,3,3,5] 27 | ``` 28 | 29 | ## Methods 30 | 31 | ### shuffle 32 | 33 | `.shuffle()` 34 | 35 | Will return a shuffled version of any array. 36 | 37 | ### zfill 38 | 39 | `.zfill(length, z = 0)` 40 | 41 | Will concat `z` to the array repeatedly until the array reaches the desired length. Inspired by python's zfill, however, the one shown here adds the zeroes at the end of the array. 42 | 43 | ### rotate 44 | 45 | `.rotate(n)` / `.rot(n)` 46 | 47 | Will rotate the array by `n` steps. Works with negative values. 48 | 49 | ## Generators 50 | 51 | ### Array.random 52 | 53 | `Array.random(length = 10, min = 0, max = 1)` 54 | 55 | Generates a new array of a given length and fills it with random values between the range `min` to `max`. 56 | 57 | ### Array.range 58 | 59 | `Array.range(start, end, step = 1)` 60 | 61 | Generates a new array of numbers starting from `start`, then each element increases by `step` until `end` (non-inclusive). 62 | 63 | ### Array.run 64 | 65 | `Array.range(end = 10, step = 1)` 66 | 67 | Generates a new array of numbers starting from 0, then each element increses by `step` until `end` (non-inclusive). -------------------------------------------------------------------------------- /doc/hydra-text.md: -------------------------------------------------------------------------------- 1 | # hydra-text 2 | 3 | A text source for Hydra 4 | 5 | ## hydraText 6 | 7 | `hydraText` are the default settings for hydra-text, which you can change: 8 | 9 | ```js 10 | hydraText.font = "serif"; 11 | 12 | // these are the defaults: 13 | window.hydraText = { 14 | font: "sans-serif", // the font-family 15 | fontStyle: "normal", // normal, bold, etc 16 | fontSize: "auto", // must be either "auto", a percentage like "90%", or pixels like "100px" 17 | textAlign: "center", // center, left or right 18 | fillStyle: "white", // a css color, CanvasGradient or CanvasPattern 19 | strokeStyle: "white", // a css color, CanvasGradient or CanvasPattern 20 | lineWidth: "2%", // a percentage of the final fontSize 21 | lineJoin: "miter", // miter, bevel or round 22 | canvasResize: 2, // a factor by which to resize the canvas that displays the text 23 | interpolation: "linear",// linear or nearest. represents the interpolation used inside hydra for the text's canvas 24 | }; 25 | ``` 26 | 27 | Settings are set to a CanvasRenderingContext2D, find more about it [here](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle). However note that the hydra-text `font` setting doesn't work the same as the context's `font` property. On hydra-text, `font` should only specify the font-family. This is because it needs to do some resizing and measurements of the text before writing it while the context's `font` property allows you to resize it. 28 | 29 | ## text 30 | 31 | `text( str, config )` 32 | 33 | * str: the text you want to write 34 | * config: either an object with the properties you want to set, or a string with the font you want 35 | 36 | ## strokeText 37 | 38 | `strokeText( str, config )` 39 | 40 | Same as `text` but draws the outline of the text instead 41 | 42 | ## strokeFillText 43 | 44 | `strokeFillText( str, config )` 45 | 46 | Fill over stroke 47 | 48 | ## fillStrokeText 49 | 50 | `fillStrokeText( str, config )` 51 | 52 | Stroke over fill -------------------------------------------------------------------------------- /doc/hydra-blend.md: -------------------------------------------------------------------------------- 1 | # hydra-blend 2 | 3 | A port of [glsl-blend](https://github.com/jamieowen/glsl-blend) to Hydra. 4 | 5 | ## Overview 6 | 7 | This extension adds most of the blending modes that you see on raster image software to Hydra. You should use them with the following syntax: `tex.blendModeName(tex2, opacity)`, where tex and tex2 are any two Hydra textures and opacity is a number (or array or function as in regular Hydra). 8 | 9 | Please read the [disclaimer about alpha here below](#important) 10 | 11 | ## List of blending modes 12 | 13 | * darken 14 | * multiply 15 | * colorBurn 16 | * linearBurn 17 | * lighten 18 | * screen 19 | * colorDodge 20 | * linearDodge 21 | * overlay 22 | * softLight 23 | * hardLight 24 | * vividLight 25 | * linearLight 26 | * pinLight 27 | * hardMix 28 | * difference 29 | * exclusion 30 | * subtract 31 | * divide 32 | * negation / negate 33 | * add2 (i didn't want to replace the regular Hydra `add`) 34 | * glow 35 | * reflect 36 | * phoenix 37 | 38 | ## About the alpha channel 39 | 40 | `glsl-blend` doesn't have any functions that work with RGBA, all function work with opaque textures of RGB. After lots of playing around with Photopea I realized that what most raster image softwares do is apply the blending over premultiplied sources and then, wherever the base texture is transparent, layer over the texture to be blended, and that's how I implemented it 41 | 42 | ### Important 43 | 44 | This extension overwrites Hydra's `layer`, `luma` and `mask` in order for them to stricly use premultiplied alpha. This means that if something doesn't look as expected, it's probably because the textures you're using aren't premultiplied and in a range from 0.0 to 1.0. `noise` will surely give you problems as it ranges from -1 to 1. 45 | 46 | I could've handled this inside the blending modes but I didn't want to taker over your ability to purposefully glitch stuff. If something doesn't look as you expect it to look you can try to premultiply it by doing `tex.premultiply()` alias `tex.pm()`. It's a function I've added that will take care of any overloaded or mismatched values. 47 | 48 | ### Note 49 | 50 | If you're interested in alpha compositing inside Hydra, please join the discussion [here](https://github.com/hydra-synth/hydra-synth/issues/109) -------------------------------------------------------------------------------- /hydra-mouse.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | function Mouse(canvas) { 30 | this.x = 0; 31 | this.y = 0; 32 | this.ax = 0; 33 | this.ay = 0; 34 | this.cx = 0; 35 | this.cy = 0; 36 | this.rx = 0; 37 | this.ry = 0; 38 | this.crx = 0; 39 | this.cry = 0; 40 | this.posx = 0; 41 | this.posy = 0; 42 | this.cposx = 0; 43 | this.cposy = 0; 44 | this.canvas = canvas; 45 | let self = this; 46 | 47 | this.handlePointerMove = function (ev) { 48 | const bound = self.canvas.getBoundingClientRect(); 49 | self.x = ev.clientX; 50 | self.y = ev.clientY; 51 | self.ax = ev.pageX; 52 | self.ay = ev.pageY; 53 | self.cx = self.ax - bound.left; 54 | self.cy = self.ay - bound.top; 55 | self.rx = self.x / window.innerWidth; 56 | self.ry = self.y / window.innerHeight; 57 | self.crx = self.cx / bound.width; 58 | self.cry = self.cy / bound.height; 59 | self.posx = -self.x / window.innerWidth + 0.5; 60 | self.posy = -self.y / window.innerHeight + 0.5; 61 | self.cposx = -self.cx / bound.width + 0.5; 62 | self.cposy = -self.cy / bound.height + 0.5; 63 | }; 64 | 65 | if (window.mouse.handlePointerMove) 66 | window.removeEventListener( 67 | "pointermove", 68 | window.mouse.handlePointerMove 69 | ); 70 | window.addEventListener("pointermove", this.handlePointerMove); 71 | } 72 | 73 | _hydraScope.mouse = new Mouse(window._hydra.canvas); 74 | -------------------------------------------------------------------------------- /hydra-arrays.js: -------------------------------------------------------------------------------- 1 | window.hydraArrays = {}; 2 | 3 | hydraArrays.newOperator = function (self, f) { 4 | return function (arr) { 5 | for (let i = 0; i < self.length; i++) { 6 | if (Array.isArray(arr)) { 7 | self[i] = arr[i] ? f(self[i], arr[i]) : self[i]; 8 | } else { 9 | self[i] = f(self[i], arr); 10 | } 11 | } 12 | return self; 13 | }; 14 | }; 15 | hydraArrays.newWrapOperator = function (self, f) { 16 | return function (arr) { 17 | for (let i = 0; i < self.length; i++) { 18 | if (Array.isArray(arr)) { 19 | self[i] = f(self[i], arr[i % arr.length]); 20 | } else { 21 | self[i] = f(self[i], arr); 22 | } 23 | } 24 | return self; 25 | }; 26 | }; 27 | 28 | // operators 29 | { 30 | const operators = { 31 | add: (x, y) => x + y, 32 | sub: (x, y) => x - y, 33 | div: (x, y) => x / y, 34 | mult: (x, y) => x * y, 35 | mod: (x, y) => x % y, 36 | }; 37 | Object.entries(operators).forEach(function ([op, f]) { 38 | Array.prototype[op] = function (arr) { 39 | return hydraArrays.newOperator(this, f)(arr); 40 | }; 41 | Array.prototype[op + "Wrap"] = function (arr) { 42 | return hydraArrays.newWrapOperator(this, f)(arr); 43 | }; 44 | }); 45 | } 46 | 47 | // methods 48 | Array.prototype.shuffle = function () { 49 | return this.sort(() => Math.random() - 0.5); 50 | }; 51 | Array.prototype.zfill = function (l, z = 0) { 52 | const _l = this.length; 53 | for (let i = 0; i < l - _l; i++) this.push(z); 54 | return this; 55 | }; 56 | Array.prototype.rotate = function (n) { 57 | const len = this.length; 58 | this.push(...this.splice(0, ((-n % len) + len) % len)); 59 | return this; 60 | }; 61 | Array.prototype.rot = Array.prototype.rotate; 62 | 63 | // generators 64 | Array.random = function (l = 10, min = 0, max = 1) { 65 | return Array.from( 66 | { length: l }, 67 | () => Math.random() * (max - min) + min 68 | ); 69 | }; 70 | Array.range = function (start, end, step = 1) { 71 | if (step === 0) return []; 72 | const length = Math.ceil((end - start) / step); 73 | return Array.from({ length }, (_, i) => start + i * step).filter( 74 | (n) => n <= end 75 | ); 76 | }; 77 | Array.run = function (end = 10, step = 1) { 78 | return Array.range(0, end, step); 79 | }; 80 | -------------------------------------------------------------------------------- /doc/hydra-outputs.md: -------------------------------------------------------------------------------- 1 | # hydra-outputs 2 | 3 | Allows users to change the settings of the framebuffers used by Hydra's outputs. The most common use case is setting framebuffers to use linear interpolation instead of the default, nearest neighbour. 4 | 5 | ### Example 6 | 7 | ```js 8 | o0.setNearest() 9 | o1.setLinear() 10 | 11 | src(o0) 12 | .layer(osc(30,.2,1).mask(shape(4,.1,0))) 13 | .scale(1.01).rotate(.01) 14 | .out(o0) 15 | 16 | src(o1) 17 | .layer(osc(30,.2,1).mask(shape(4,.1,0))) 18 | .scale(1.01).rotate(.01) 19 | .out(o1) 20 | 21 | src(o0) 22 | .layer(src(o1).mask(shape(1,0,0).rotate(Math.PI/2))) 23 | .out(o2) 24 | 25 | render(o2) 26 | ``` 27 | 28 | [Open in Hydra!](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtb3V0cHV0cy5qcyUyMiklMEElMEFvMC5zZXROZWFyZXN0KCklMEFvMS5zZXRMaW5lYXIoKSUwQSUwQXNyYyhvMCklMEElMDkubGF5ZXIob3NjKDMwJTJDLjIlMkMxKS5tYXNrKHNoYXBlKDQlMkMuMSUyQzApKSklMEElMDkuc2NhbGUoMS4wMSkucm90YXRlKC4wMSklMEElMDkub3V0KG8wKSUwQSUwQXNyYyhvMSklMEElMDkubGF5ZXIob3NjKDMwJTJDLjIlMkMxKS5tYXNrKHNoYXBlKDQlMkMuMSUyQzApKSklMEElMDkuc2NhbGUoMS4wMSkucm90YXRlKC4wMSklMEElMDkub3V0KG8xKSUwQSUwQXNyYyhvMCklMEElMDkubGF5ZXIoc3JjKG8xKS5tYXNrKHNoYXBlKDElMkMwJTJDMCkucm90YXRlKE1hdGguUEklMkYyKSkpJTBBJTA5Lm91dChvMiklMEElMEFyZW5kZXIobzIp) 29 | 30 | --- 31 | 32 | ## How to use 33 | 34 | This extensions extends the `Output` prototype, so it adds methods to all outputs such as `o0`, `o1`, etc. It also adds a new `oS` object, which lets you use these methods on all outputs at the same time. 35 | 36 | ### Methods 37 | 38 | | Method | Description | 39 | |---|---| 40 | | o0.setLinear() | Sets the interpolation method to linear. Looks smooth. | 41 | | o0.setNearest()| Sets the interpolation method to nearest neighbour. Looks pixelated. | 42 | | o0.setFbos(fbo0, fbo1) | Lets you set any of [the texture properties regl allows](https://github.com/regl-project/regl/blob/master/API.md#texture-constructor). If you only set fbo0, fbo1 will copy those settings. | 43 | 44 | * Remember you can change all outputs at the same time as such: `oS.setLinear()` 45 | * You may want to use setFbos to set different interpolation methods for `mag` and `min`. For example: `oS.setFbos({ mag: 'linear', min: 'nearest' })` 46 | 47 | #### Wrapping methods 48 | 49 | If you want to change Hydra's wrapping mode I'd recommend using hydra-wrap. But if you're sure that what you want to do is change the fbos wrapping settings you may also use the following methods. 50 | 51 | | Method | Description | 52 | |---|---| 53 | | o0.setRepeat() | Sets the wrapping to repeat | 54 | | o0.setMirror() | Sets the wrapping to mirror | 55 | | o0.setClamp() | Sets the wrapping to clamp | 56 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # utility scripts 2 | 3 | These are some scripts I've come up with to have some sort of framework to make the extensions compatible with most scenarios and between each other: 4 | 5 | ## Find the HydraRenderer object 6 | 7 | Finds hydra and assigns it to `window._hydra`. Will also get the scope of the generator functions and assign it to `window._hydraScope` so that you can code extensions compatible with instance mode. All following scripts depend on this one to be run first. 8 | 9 | ```js 10 | { 11 | const getHydra = function () { 12 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 13 | ? "editor" 14 | : window.atom?.packages 15 | ? "atom" 16 | : "idk"; 17 | if (whereami === "editor") { 18 | return window.hydraSynth; 19 | } 20 | if (whereami === "atom") { 21 | return global.atom.packages.loadedPackages["atom-hydra"] 22 | .mainModule.main.hydra; 23 | } 24 | let _h = [ 25 | window.hydraSynth, 26 | window._hydra, 27 | window.hydra, 28 | window.h, 29 | window.H, 30 | window.hy 31 | ].find(h => h?.regl); 32 | return _h; 33 | }; 34 | window._hydra = getHydra(); 35 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 36 | } 37 | ``` 38 | 39 | Tip: Always call `setFunction` from `_hydra.synth.setFunction` 40 | 41 | ## Get prototypes 42 | 43 | ```js 44 | const gS = _hydraScope.osc().constructor.prototype; // GlslSource prototype 45 | const oP = _hydra.o[0].constructor.prototype; // Output prototype 46 | const hS = _hydra.s[0].constructor.prototype; // HydraSource prototype 47 | ``` 48 | 49 | ## Custom update chain 50 | 51 | The following script unlinks the reference from `window.update` to `_hydra.synth.update`, replacing it with another placeholder. It then defines a `_updateChain` array of functions which will then be ran by the actual `_hydra.synth.update`. This allows you to inject new functions into the hydra tick loop via pushing to them the `_updateChain` array. Take into account other extensions might also push, so always keep track of your function's indexes in the array yourself. 52 | 53 | ```js 54 | { 55 | if (!window._updateChain) { 56 | window.update = window.update || ((dt) => { }); 57 | window._updateChain = [() => window["update"]()]; 58 | _hydra.sandbox.userProps = ["speed", "bpm", "fps"]; 59 | _hydra.synth.update = (dt) => { 60 | for (func of window._updateChain) { 61 | func(dt); 62 | } 63 | }; 64 | } 65 | } 66 | ``` 67 | -------------------------------------------------------------------------------- /hydra-vec4.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | function filterVecArgs(args) { 31 | let pass = []; 32 | args.forEach((arg) => { 33 | if (typeof arg == "number" || typeof arg == "function") 34 | pass = pass.concat(arg); 35 | else if (arg.constructor.name == "Array") { 36 | if (arg._vec) pass = pass.concat(arg); 37 | else pass = pass.concat([arg]); 38 | } 39 | }); 40 | return pass; 41 | } 42 | window.vec4 = function (...args) { 43 | args = filterVecArgs(args); 44 | if (args.length == 1) { 45 | const arg = args[0]; 46 | return solid(arg, arg, arg, arg); 47 | } else if (args.length == 4) { 48 | return _hydraScope.solid(...args); 49 | } else { 50 | throw new Error("vec4 should receive 4 elements"); 51 | } 52 | }; 53 | window.vec3 = function (...args) { 54 | args = filterVecArgs(args); 55 | if (args.length == 1) { 56 | const arg = args[0]; 57 | let ret = Array.from({ length: 3 }, () => arg); 58 | ret._vec = true; 59 | return ret; 60 | } else if (args.length == 3) { 61 | args._vec = true; 62 | return args; 63 | } else { 64 | throw new Error("vec3 should receive 3 elements"); 65 | } 66 | }; 67 | window.vec2 = function (...args) { 68 | args = filterVecArgs(args); 69 | if (args.length == 1) { 70 | const arg = args[0]; 71 | let ret = Array.from({ length: 2 }, () => arg); 72 | ret._vec = true; 73 | return ret; 74 | } else if (args.length == 2) { 75 | args._vec = true; 76 | return args; 77 | } else { 78 | throw new Error("vec2 should receive 2 elements"); 79 | } 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /hydra-outputs.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const oP = _hydra.o[0].constructor.prototype; 31 | oP.fboSettings = Array(2).fill({ 32 | mag: "nearest", 33 | min: "nearest", 34 | width: width, 35 | height: height, 36 | format: "rgba", 37 | }); 38 | oP.setFbos = function (fbo0, fbo1) { 39 | var colors = fbo1 ? [fbo0, fbo1] : [fbo0, fbo0]; 40 | this.fboSettings = colors.map((x, i) => { 41 | return { 42 | ...this.fboSettings[i], 43 | width: width, 44 | height: height, 45 | ...x, 46 | }; 47 | }); 48 | this.fbos = this.fboSettings.map((x) => 49 | this.regl.framebuffer({ 50 | color: this.regl.texture(x), 51 | depthStencil: false, 52 | }) 53 | ); 54 | }; 55 | oP.setNearest = function () { 56 | this.setFbos({ mag: "nearest", min: "nearest" }); 57 | }; 58 | oP.setLinear = function () { 59 | this.setFbos({ mag: "linear", min: "linear" }); 60 | }; 61 | oP.setRepeat = function () { 62 | this.setFbos({ wrapS: "repeat", wrapT: "repeat" }); 63 | }; 64 | oP.setClamp = function () { 65 | this.setFbos({ wrapS: "clamp", wrapT: "clamp" }); 66 | }; 67 | oP.setMirror = function () { 68 | this.setFbos({ wrapS: "mirror", wrapT: "mirror" }); 69 | }; 70 | _hydraScope.oS = { outputs: window._hydra.o }; 71 | _hydraScope.oS.setNearest = function () { 72 | this.outputs.forEach((x) => x.setNearest()); 73 | }; 74 | _hydraScope.oS.setLinear = function () { 75 | this.outputs.forEach((x) => x.setLinear()); 76 | }; 77 | _hydraScope.oS.setRepeat = function () { 78 | this.outputs.forEach((x) => x.setWrap()); 79 | }; 80 | _hydraScope.oS.setClamp = function () { 81 | this.outputs.forEach((x) => x.setClamp()); 82 | }; 83 | _hydraScope.oS.setMirror = function () { 84 | this.outputs.forEach((x) => x.setMirror()); 85 | }; 86 | _hydraScope.oS.setFbos = function (_x, y) { 87 | this.outputs.forEach((x) => x.setFbos(_x, y)); 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /doc/hydra-arithmetics.md: -------------------------------------------------------------------------------- 1 | # hydra-arithmetics 2 | 3 | Adds many functions related to visual arithmetics. 4 | 5 | ## Operators 6 | 7 | All operators in this extension are actually wrappers that let you operate by either a number (or array or function as with regular hydra) or a texture. This basically means you can both do `osc().add(noise())` or `osc().add(1)` and both will work as expected. 8 | 9 | ### div 10 | 11 | `.div( divisor )` 12 | 13 | Divide a texture by some value or texture 14 | 15 | ### add, sub, mult 16 | 17 | | single value | texture | 18 | |----------------|------------------------| 19 | |`.add( value )` | `.add( tex , amount )` | 20 | |`.sub( value )` | `.sub( tex , amount )` | 21 | |`.mult( value )`| `.mult( tex , amount )`| 22 | 23 | These act the same as in regular Hydra, with the added functionality of working with regular values too. 24 | 25 | ### mod 26 | 27 | `.mod( x )` 28 | 29 | Calculates the modulo `x` for the texture that calls it. 30 | 31 | ### min, max 32 | 33 | `.min( x )`, `.max( x )` 34 | 35 | Returns the minimum or maximum value when compared to the texture that calls it, accordingly. 36 | 37 | ### step 38 | 39 | `.step( x )` 40 | 41 | Generates a step function by comparing x to the texture that calls it. 42 | 43 | ### amp, offset 44 | 45 | `.amp( singleValue ) == .amplitude( singleValue )`, `.offset( singleValue ) == .off( singleValue )` 46 | 47 | These are basically aliases for `.mult()` and `.add()` for single values. 48 | 49 | --- 50 | 51 | ## Bipolar and unipolar 52 | 53 | ### bipolar 54 | 55 | `.bipolar( amp = 1 )` 56 | 57 | Takes unipolar values (values ranged from 0 to 1) and turns them to bipolar (ranged from -1 to 1). The `amp` argument let's you multiply the result by some value if needed. 58 | 59 | ### unipolar 60 | 61 | `.unipolar( amp = 1 )` 62 | 63 | Takes bipolar values (-1 to 1) and turns them to unipolar (0 to 1). The `amp` argument let's you multiply the result by some value if needed. 64 | 65 | --- 66 | 67 | ## Ranges 68 | 69 | ### range 70 | 71 | `.range( min = 0, max = 1 )` 72 | 73 | Allows you to change the range the range of unipolar values to one between `min` and `max`. 74 | 75 | ### birange 76 | 77 | `.birange( min = 0, max = 1 )` 78 | 79 | Allows you to change the range the range of bipolar values to one between `min` and `max`. 80 | 81 | ### clamp 82 | 83 | `.clamp( min = 0, max = 1 )` 84 | 85 | `clamp` will constrain values between a range from `min` to `max`. 86 | 87 | --- 88 | 89 | ## Other transformations 90 | 91 | This extension also adds many argument-less transforms. You can use them as such, for example: `osc().abs()`. 92 | 93 | ### abs, fract, sign 94 | 95 | #### abs 96 | 97 | Returns the absolute value. 98 | 99 | #### fract 100 | 101 | Returns only the fractional part of a value. 102 | 103 | #### sign 104 | 105 | Sign function, will return 1 for positive values and -1 for negative values. 106 | 107 | ### Trigonometic functions 108 | 109 | The following are included: `sin`, `cos`, `tan`, `asin`, `acos`, `atan` 110 | 111 | ### Exponential and rational functions 112 | 113 | The following are included: `exp`, `log`, `exp2`, `log2`, `sqrt`, `inversesqrt` -------------------------------------------------------------------------------- /old/hydra-nowrap.js: -------------------------------------------------------------------------------- 1 | const fboSettings = Array(2).fill({ 2 | mag: "nearest", 3 | min: "nearest", 4 | width: width, 5 | height: height, 6 | wrapS: 'clamp', 7 | wrapT: 'clamp', 8 | format: "rgba" 9 | }); 10 | choo.state.hydra.hydra.o.forEach((output) => { 11 | output.fbos = fboSettings.map((x) => 12 | output.regl.framebuffer({ 13 | color: output.regl.texture(x), 14 | depthStencil: false, 15 | }) 16 | ); 17 | }); 18 | 19 | [ 20 | { 21 | name: "prev", 22 | type: "src", 23 | inputs: [], 24 | glsl: `return texture2D(prevBuffer, _st);`, 25 | }, 26 | { 27 | name: "src", 28 | type: "src", 29 | inputs: [ 30 | { 31 | type: "sampler2D", 32 | name: "tex", 33 | default: NaN, 34 | }, 35 | ], 36 | glsl: `return texture2D(tex, _st);`, 37 | }, 38 | { 39 | name: "scroll", 40 | type: "coord", 41 | inputs: [ 42 | { 43 | type: "float", 44 | name: "scrollX", 45 | default: 0.5, 46 | }, 47 | { 48 | type: "float", 49 | name: "scrollY", 50 | default: 0.5, 51 | }, 52 | { 53 | type: "float", 54 | name: "speedX", 55 | default: 0, 56 | }, 57 | { 58 | type: "float", 59 | name: "speedY", 60 | default: 0, 61 | }, 62 | ], 63 | glsl: `_st.x += scrollX + time*speedX; _st.y += scrollY + time*speedY; return _st;`, 64 | }, 65 | { 66 | name: "scrollX", 67 | type: "coord", 68 | inputs: [ 69 | { 70 | type: "float", 71 | name: "scrollX", 72 | default: 0.5, 73 | }, 74 | { 75 | type: "float", 76 | name: "speed", 77 | default: 0, 78 | }, 79 | ], 80 | glsl: `_st.x += scrollX + time*speed; return _st;`, 81 | }, 82 | { 83 | name: "modulateScrollX", 84 | type: "combineCoord", 85 | inputs: [ 86 | { 87 | type: "float", 88 | name: "scrollX", 89 | default: 0.5, 90 | }, 91 | { 92 | type: "float", 93 | name: "speed", 94 | default: 0, 95 | }, 96 | ], 97 | glsl: `_st.x += _c0.r*scrollX + time*speed; return _st;`, 98 | }, 99 | { 100 | name: "scrollY", 101 | type: "coord", 102 | inputs: [ 103 | { 104 | type: "float", 105 | name: "scrollY", 106 | default: 0.5, 107 | }, 108 | { 109 | type: "float", 110 | name: "speed", 111 | default: 0, 112 | }, 113 | ], 114 | glsl: `_st.y += scrollY + time*speed; return _st;`, 115 | }, 116 | { 117 | name: "modulateScrollY", 118 | type: "combineCoord", 119 | inputs: [ 120 | { 121 | type: "float", 122 | name: "scrollY", 123 | default: 0.5, 124 | }, 125 | { 126 | type: "float", 127 | name: "speed", 128 | default: 0, 129 | }, 130 | ], 131 | glsl: ` _st.y += _c0.r*scrollY + time*speed; return _st;`, 132 | }, 133 | ].forEach((x) => setFunction(x)); 134 | 135 | console.log("hydra-nowrap is deprecated, I recomment using hydra-wrap and setting the wrapping to nowrap") -------------------------------------------------------------------------------- /hydra-fractals.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | [ 30 | { 31 | name: "mirrorX", 32 | type: "coord", 33 | inputs: [ 34 | { 35 | type: "float", 36 | name: "pos", 37 | default: 0, 38 | }, 39 | { 40 | type: "float", 41 | name: "coverage", 42 | default: 1, 43 | }, 44 | ], 45 | glsl: `_st.x = (0.0-abs(fract(_st.x/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`, 46 | }, 47 | { 48 | name: "mirrorY", 49 | type: "coord", 50 | inputs: [ 51 | { 52 | type: "float", 53 | name: "pos", 54 | default: 0, 55 | }, 56 | { 57 | type: "float", 58 | name: "coverage", 59 | default: 1, 60 | }, 61 | ], 62 | glsl: `_st.y = (0.0-abs(fract(_st.y/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`, 63 | }, 64 | { 65 | name: "mirrorX2", 66 | type: "coord", 67 | inputs: [ 68 | { 69 | type: "float", 70 | name: "pos", 71 | default: 0, 72 | }, 73 | { 74 | type: "float", 75 | name: "coverage", 76 | default: 1, 77 | }, 78 | ], 79 | glsl: `_st.x = (abs(fract(_st.x/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`, 80 | }, 81 | { 82 | name: "mirrorY2", 83 | type: "coord", 84 | inputs: [ 85 | { 86 | type: "float", 87 | name: "pos", 88 | default: 0, 89 | }, 90 | { 91 | type: "float", 92 | name: "coverage", 93 | default: 1, 94 | }, 95 | ], 96 | glsl: `_st.y = (0.0-abs(fract(_st.y/coverage)-(1.0-0.5-pos))+0.5-pos)*coverage; return _st;`, 97 | }, 98 | { 99 | name: "mirrorWrap", 100 | type: "coord", 101 | inputs: [], 102 | glsl: `return -abs(fract(_st/2.0)*2.0-1.0)+1.0;`, 103 | }, 104 | { 105 | name: "inversion", 106 | type: "coord", 107 | inputs: [], 108 | glsl: `_st /= dot(_st,_st); return _st;`, 109 | }, 110 | ].forEach((x) => _hydra.synth.setFunction(x)); 111 | -------------------------------------------------------------------------------- /hydra-swizzle.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const gS = _hydraScope.osc().constructor.prototype; 31 | // https://stackoverflow.com/questions/34127294/ 32 | function getSwizzles(coords) { 33 | function combinations(input, length, curstr) { 34 | if (curstr.length == length) return [curstr]; 35 | var ret = []; 36 | for (var i = 0; i < input.length; i++) { 37 | ret.push.apply( 38 | ret, 39 | combinations(input, length, curstr + input[i]) 40 | ); 41 | } 42 | return ret; 43 | } 44 | 45 | let ret = combinations(coords, coords.length, ""); 46 | ret.splice(ret.indexOf("input"), 1); 47 | return ret; 48 | } 49 | const threeComponents = [].concat(getSwizzles("rgb"), getSwizzles("xyz")); 50 | 51 | const fourComponents = [].concat(getSwizzles("rgba"), getSwizzles("xyzw")); 52 | 53 | function definePropertyFromMethod(method, newName = "") { 54 | newName = newName ? newName : method; 55 | Object.defineProperty(gS, newName, { 56 | configurable: true, 57 | get() { 58 | return this[method].bind(this)(); 59 | }, 60 | }); 61 | } 62 | 63 | function returnSwizzleKeepingAlpha(swizzle){ 64 | const containsAny = (str, chars) => [...chars].some(char => str.includes(char)); 65 | const alpha = containsAny(swizzle, "rgb") ? "a" : "w"; 66 | return `return _c0.${swizzle + alpha};`; 67 | } 68 | 69 | threeComponents.forEach((swizzle) => { 70 | const name = "_" + swizzle; 71 | _hydra.synth.setFunction({ 72 | name, 73 | type: "color", 74 | inputs: [], 75 | glsl: returnSwizzleKeepingAlpha(swizzle), 76 | }); 77 | definePropertyFromMethod(name, swizzle); 78 | }); 79 | fourComponents.forEach((swizzle) => { 80 | const name = "_" + swizzle; 81 | _hydra.synth.setFunction({ 82 | name, 83 | type: "color", 84 | inputs: [], 85 | glsl: `return _c0.${swizzle};`, 86 | }); 87 | definePropertyFromMethod(name, swizzle); 88 | }); 89 | Array.from("rgbaxyzw").forEach((elem) => { 90 | const name = "_swizzle_" + elem; 91 | _hydra.synth.setFunction({ 92 | name, 93 | type: "color", 94 | inputs: [], 95 | glsl: returnSwizzleKeepingAlpha(elem.repeat(3)), 96 | }); 97 | definePropertyFromMethod(name, elem); 98 | }); 99 | } 100 | -------------------------------------------------------------------------------- /doc/ES/hydra-glsl.md: -------------------------------------------------------------------------------- 1 | # hydra-glsl 2 | 3 | Este hack/extensión te permite codear GLSL casi que directamente adentro de Hydra. Añade 5 functiones, 1 por cada tipo de glsl-function que hay en Hydra 4 | 5 | ## Ejemplo: 6 | ```js 7 | glsl('vec4(sin(((_st.x*54.)+time*2.)*vec3(0.1,0.102,0.101)),1.0)') 8 | .diff(o0) 9 | .glslColor('vec4(c0.brg,1.)') 10 | .glslCoord('xy*=(1.0/vec2(i0, i0)); return xy',.25) 11 | .glslCombine('return c0-c1',o1) 12 | .glslCombineCoord('uv+(vec2(c0.r,c0.b)*0.1)',o1) 13 | .out() 14 | ``` 15 | [Miralo en hydra!](https://hydra.ojack.xyz/?sketch_id=agiUw1vmrGmmf4Zy) 16 | 17 | --- 18 | 19 | ## Funciones: 20 | 21 | | función | argumentos dsps del código | tipo correspondiente | argumentos de glsl : aliases | 22 | |--------------------------|------------------|--------------------|-----------------------------------| 23 | | glsl() | ...args | 'src' | _st : st, uv, xy | 24 | | osc().glslColor() | ...args | 'color' | _c0 : c0, color | 25 | | osc().glslCoord() | ...args | 'coord' | _st : st, uv, xy | 26 | | osc().glslCombine() | texture, ...args | 'combine' | _c0 : c0, color0; _c1: c1, color1 | 27 | | osc().glslCombineCoord() | texture, ...args | 'combineCoord' | _st : st, uv, xy; _c0: c0, color | 28 | 29 | *Nótese que `osc()` es meramente un ejemplo* 30 | 31 | ### Extra functions 32 | 33 | | función | argumentos dsps del código | tipo correspondiente | descripción | 34 | |--------------------------|------------------|--------------------|-----------------------------------| 35 | | osc().glslHsv() | ...args | 'color' | Convierte los colores de c0 a HSV. Podés acceder y modificar estos valores desde un `vec3 hsv` | 36 | 37 | --- 38 | 39 | ## Argumentos 40 | 41 | Cada función trae con sigo 10 argumentos predeterminados con nombres `i0`,`i1`,`i2`...`i9`. Todos estos se inicializan con el valor por default de `1`. Podés usar estos 10 argumentos o definir los tuyos al mandar cada argumento en un array con un formate de `['nombre',valor]`. 42 | 43 | #### Importante: 44 | Todos los argumentos son del tipo `float`. 45 | 46 | ### Ejemplo: 47 | 48 | ```js 49 | glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) 50 | .glslColor('vec4(c0.brg-(sin(c0.b)*miVariable),1.)',['miVariable',0.2]) 51 | .out() 52 | ``` 53 | 54 | Hasta podés mandar funciones como variables como se suele hacer en Hydra: 55 | 56 | ```js 57 | glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) 58 | .glslColor('vec4(c0.brg-(sin(c0.b)*miVariable),1.)',['miVariable',()=>time%1]) 59 | .out() 60 | ``` 61 | 62 | --- 63 | 64 | ## Sobre el codigo (y el lazy code) 65 | 66 | * Podés usar de una cualquiera de los aliases descritos arriba. 67 | * Podés omitir el semicolón al final. 68 | * Podés omitir la palabra reservada "return". 69 | * Podes mandar código que ocupe múltiples líneas. 70 | * Solo podés omitir el semicolón en la ultima linea. 71 | * Podés aún así omitir la palabra return. 72 | * Podés escribir tus propias `c0`s, `st`s o cualquier otra variable llamada como alguna de los aliases de arriba. El script se fija que no hayas instanciado ninguna variable llamada así antes de definir los aliases. 73 | * No podes usar el mismo nombre de un alias como el nombre de un argumento 74 | 75 | --- 76 | 77 | ## Tip 78 | 79 | Tené la consola del browser abierta. Hydra no tira errores de frag a la consola integrada. 80 | 81 | ## Nota 82 | 83 | Este hack funciona llenando de nuevas funciones al contexto global (`window`) y/o al constructor de GlslSource. No te asustes si ves un montón de funciones llamdas `glsl_ext_NODE_`-algo. 84 | -------------------------------------------------------------------------------- /doc/hydra-glsl.md: -------------------------------------------------------------------------------- 1 | # hydra-glsl 2 | 3 | This hack/extension allows you to use glsl code almost directly inside Hydra. It adds 5 functions, 1 according to each type of glsl-function in hydra. 4 | 5 | ## Example 6 | 7 | ```js 8 | glsl('vec4(sin(((_st.x*54.)+time*2.)*vec3(0.1,0.102,0.101)),1.0)') 9 | .diff(o0) 10 | .glslColor('vec4(c0.brg,1.)') 11 | .glslCoord('xy*=(1.0/vec2(i0, i0)); return xy',.25) 12 | .glslCombine('return c0-c1',o1) 13 | .glslCombineCoord('uv+(vec2(c0.r,c0.b)*0.1)',o1) 14 | .out() 15 | ``` 16 | 17 | [Open in hydra!](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCdodHRwcyUzQSUyRiUyRmh5cGVyLWh5ZHJhLmdsaXRjaC5tZSUyRmh5ZHJhLWdsc2wuanMnKSUwQSUwQSUyRiUyRmh5ZHJhLWdsc2wlMEElMkYlMkZFeHRlbnNpb24lMjBmb3IlMjBjb2RpbmclMjBnbHNsJTIwb24lMjB0aGUlMjBmbHklMjBpbnNpZGUlMjBIeWRyYSUyMGNvZGUlMEElMkYlMkZieSUyMFJJVENIU0UlMEElMkYlMkZtb3JlJTIwaW5mbyUyMGFuZCUyMGRvY3MlM0ElMEElMkYlMkYlMDlodHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZyaXRjaHNlJTJGaHlkcmEtZXh0ZW5zaW9ucyUyRmJsb2IlMkZtYWluJTJGZG9jJTJGaHlkcmEtZ2xzbC5tZCUwQSUwQWdsc2woJ3ZlYzQoc2luKCgoX3N0LngqNTQuKSUyQnRpbWUqMi4pKnZlYzMoMC4xJTJDMC4xMDIlMkMwLjEwMSkpJTJDMS4wKScpJTBBJTIwJTIwJTA5LmRpZmYobzApJTBBJTA5Lmdsc2xDb2xvcigndmVjNChjMC5icmclMkMxLiknKSUwQSUwOS5nbHNsQ29vcmQoJ3h5KiUzRCgxLjAlMkZ2ZWMyKGkwJTJDJTIwaTApKSUzQiUyMHJldHVybiUyMHh5JyUyQy4yNSklMEElMDkuZ2xzbENvbWJpbmUoJ3JldHVybiUyMGMwLWMxJyUyQ28xKSUwQSUwOS5nbHNsQ29tYmluZUNvb3JkKCd1diUyQih2ZWMyKGMwLnIlMkNjMC5iKSowLjEpJyUyQ28xKSUwQSUyMCUyMC5vdXQoKSUwQSUwQW5vaXNlKDMpJTBBJTA5Lm91dChvMSklMEE%3D) 18 | 19 | --- 20 | 21 | ## Functions 22 | 23 | | function | arguments after code | corresponding type | glsl arguments : aliases | 24 | |--------------------------|------------------|--------------------|-----------------------------------| 25 | | glsl() | ...args | 'src' | _st : st, uv, xy | 26 | | osc().glslColor() | ...args | 'color' | _c0 : c0, color | 27 | | osc().glslCoord() | ...args | 'coord' | _st : st, uv, xy | 28 | | osc().glslCombine() | texture, ...args | 'combine' | _c0 : c0, color0;_c1: c1, color1 | 29 | | osc().glslCombineCoord() | texture, ...args | 'combineCoord' | _st : st, uv, xy;_c0: c0, color | 30 | 31 | *Note that `osc()` is merely an example* 32 | 33 | ### Extra functions 34 | 35 | | function | arguments after code | corresponding type | description | 36 | |--------------------------|------------------|--------------------|-----------------------------------| 37 | | osc().glslHsv() | ...args | 'color' | Converts to and from HSV. With your code being placed in between. Pre-defines the HSV converted values as a `vec3 hsv`| 38 | 39 | --- 40 | 41 | ## Arguments 42 | 43 | Each function comes with 10 predefined inputs called `i0`,`i1`,`i2`...`i9`. All of which are defaulted to `1`. You can use either these 10 arguments or define your own by sending them as arrays in the form of `['name',value]`. 44 | 45 | #### Important 46 | 47 | All inputs are `float` as of now. 48 | 49 | ### Example 50 | 51 | ```js 52 | glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) 53 | .glslColor('vec4(c0.brg-(sin(c0.b)*myVariable),1.)',['myVariable',0.2]) 54 | .out() 55 | ``` 56 | 57 | You can even send arguments as functions in typical Hydra manner: 58 | 59 | ```js 60 | glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) 61 | .glslColor('vec4(c0.brg-(sin(c0.b)*myVariable),1.)',['myVariable',()=>time%1]) 62 | .out() 63 | ``` 64 | 65 | --- 66 | 67 | ## About the code (and lazy code) 68 | 69 | * You can straight up use any of the aliases mentioned above. 70 | * You can omit the semicolon at the end. 71 | * You can omit the return keyword. 72 | * You can use multiline code. 73 | * You can only omit the semicolon in the last line. 74 | * You can still omit the return keyword. 75 | * You can write your own `c0`s, `st`s or any other variables named like aliases mentioned above. The script checks if you defined any variables named like that before assigning the aliases. 76 | * You cannot use aliases as names of custom arguments 77 | 78 | --- 79 | 80 | ## Tip 81 | 82 | Have the browser's console open. Hydra doesn't show frag errors on the built-in console. 83 | 84 | ## Note 85 | 86 | This hack works by flooding either the global context (the `window`) or the GlslSource constructor with new Hydra functions. Don't be scared to find lots of functions called `glsl_ext_NODE_`-something. 87 | -------------------------------------------------------------------------------- /doc/hydra-pixels.md: -------------------------------------------------------------------------------- 1 | # hydra-pixels 2 | 3 | --- 4 | This extension adds a functionality to each Hydra output that allows you to read the content of the pixels displayed. 5 | 6 | ## Example 7 | 8 | ```js 9 | osc(40,.09,1.5) 10 | .diff(osc(20).luma()) 11 | .out() 12 | 13 | oct = ()=> shape(8,.3).diff(shape(8,.28)).luma() 14 | src(o0) 15 | .layer( 16 | oct() 17 | .color(1,0,0) 18 | .scale(()=>1+(pixel[0]/255)) 19 | ) 20 | .layer( 21 | oct() 22 | .color(0,0,1) 23 | .scale(()=>1+(pixel[2]/255)) 24 | ) 25 | .layer(src(o0).scale(128).mask(shape(4,.03,0))) 26 | .out(o1) 27 | 28 | render(o1) 29 | 30 | update = ()=> { 31 | pixel = o0.read(width/2,height/2) // center of the screen 32 | } 33 | ``` 34 | 35 | [open in hydra](https://hydra.ojack.xyz/?code=JTJGJTJGJTIwbGljZW5zZWQlMjB3aXRoJTIwQ0MlMjBCWS1OQy1TQSUyMDQuMCUyMGh0dHBzJTNBJTJGJTJGY3JlYXRpdmVjb21tb25zLm9yZyUyRmxpY2Vuc2VzJTJGYnktbmMtc2ElMkY0LjAlMkYlMEElMkYlMkZoeWRyYS1waXhlbHMlMEElMkYlMkZyZWFkJTIwcGl4ZWxzJTIwZnJvbSUyMGVhY2glMjBoeWRyYSUyMG91dHB1dCUwQSUyRiUyRmJ5JTIwUklUQ0hTRSUwQSUwQWF3YWl0JTIwbG9hZFNjcmlwdCgnaHR0cHMlM0ElMkYlMkZoeXBlci1oeWRyYS5nbGl0Y2gubWUlMkZoeWRyYS1waXhlbHMuanMnKSUwQSUwQW9zYyg0MCUyQy4wOSUyQzEuNSklMEElMDkuZGlmZihvc2MoMjApLmx1bWEoKSklMEEub3V0KCklMEElMEFvY3QlMjAlM0QlMjAoKSUzRCUzRSUyMHNoYXBlKDglMkMuMykuZGlmZihzaGFwZSg4JTJDLjI4KSkubHVtYSgpJTBBc3JjKG8wKSUwQSUwOS5sYXllciglMEElMjAlMjAlMDklMDlvY3QoKSUwQSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUwOS5jb2xvcigxJTJDMCUyQzApJTBBJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTIwJTA5LnNjYWxlKCgpJTNEJTNFMSUyQihwaXhlbCU1QjAlNUQlMkYyNTUpKSUwQSUyMCUyMCUyMCUyMCklMEElMjAlMjAlMDkubGF5ZXIoJTBBJTIwJTIwJTA5JTA5b2N0KCklMEElMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMjAlMDkuY29sb3IoMCUyQzAlMkMxKSUwQSUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUyMCUwOS5zY2FsZSgoKSUzRCUzRTElMkIocGl4ZWwlNUIyJTVEJTJGMjU1KSklMEElMjAlMjAlMjAlMjApJTBBJTA5LmxheWVyKHNyYyhvMCkuc2NhbGUoMTI4KS5tYXNrKHNoYXBlKDQlMkMuMDMlMkMwKSkpJTBBJTA5Lm91dChvMSklMEElMEFyZW5kZXIobzEpJTBBJTBBdXBkYXRlJTIwJTNEJTIwKCklM0QlM0UlMjAlN0IlMEElMjAlMjBwaXhlbCUyMCUzRCUyMG8wLnJlYWQoaW5uZXJXaWR0aCUyRjIlMkNpbm5lckhlaWdodCUyRjIpJTBBJTdEJTBBJTJGJTJGJTIweW91JTIwSEFWRSUyMHRvJTIwdXNlJTIwdGhlJTIwdXBkYXRlJTIwZnVuY3Rpb24lMjAlMEElMkYlMkYlMjBhbmQlMjBhc3NpZ24lMjBwaXhlbHMlMjB0byUyMHZhcmlhYmxlcyUyMGJlZm9yZSUyMHVzaW5nJTIwdGhlbSUyMGFib3ZlJTBBJTJGJTJGJTIwaWYlMjB5b3UlMjBkb24ndCUyQyUyMGxldCdzJTIwc2F5JTIweW91JTIwdHJ5JTIwb3NjKCkucm90YXRlKCgpJTNEJTNFbzAucmVhZCgpJTVCMCU1RCklMkMlMEElMkYlMkYlMjB5b3UnbGwlMjBtYWtlJTIwYSUyMGZlZWRiYWNrJTIwbG9vcCUyMGJldHdlZW4lMjBmcmFtZWJ1ZmZlcnMlMjBuJTIwc3R1ZmYlMEElMEElMEElMEElMEE%3D) 36 | 37 | --- 38 | 39 | ## Functions 40 | 41 | `Output` refers to a hydra output such as `o0`, `o1`, etc. 42 | 43 | ### Output.read() 44 | 45 | `Output.read( x = 0, y = 0, w = 1, h = 1 )` 46 | 47 | * `x` :: x position from which to start reading 48 | * `y` :: y position from which to start reading 49 | * `w` :: width of the area to be read 50 | * `y` :: height of the area to be read 51 | 52 | By default this function returns only the pixel at the position specified by the two first arguments. 53 | 54 | ### Output.readAll() 55 | 56 | `Output.readAll()` 57 | 58 | Returns all pixels in the output. 59 | 60 | ## How to use them 61 | 62 | Any of the mentioned functions will return an array (actually, a Uint8Array) with all the pixels components in order. For example, retrieving one pixel with return something like `[255,0,20,240]`, each value corresponding to rgba accordingly. Retrieving two pixels will return something like `[255,0,20,240,250,0,10,240]`, with the rgba components of each pixel every 4 elements. Users of p5 will be familiar with this way of using arrays. 63 | 64 | ### Making Hydra react to pixels 65 | 66 | If you want Hydra to react to pixels you can't simply `osc().rotate(()=>o0.read()[0]).out()`, this will create a feedback between framebuffers or something like that (it throws an error and doesn't do what it's expected, that's the important bit innit?). In order to retrieve values every frame you have to first assign them to a variable in Hydra's `update` function. This is a function that runs every time a frame is processed, similar to p5's `draw`. The [example](#example) above shows this in action in the following bit: 67 | 68 | ```js 69 | update = ()=> { 70 | pixel = o0.read(innerWidth/2,innerHeight/2) // center of the screen 71 | } 72 | ``` 73 | 74 | Now you can use `pixel` in your Hydra code. 75 | 76 | ### Warning 77 | 78 | Sending `read` values bigger than the width or height of the canvas as xy positions, or exceeding the canvas when sending values for the width and height of the area to read, can crash Hydra entirely. 79 | -------------------------------------------------------------------------------- /hydra-gradientmap.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | _hydra.synth.setFunction({ 30 | name: "lookupX", 31 | type: "color", 32 | inputs: [ 33 | { 34 | name: "_tex", 35 | type: "sampler2D", 36 | default: o0, 37 | }, 38 | { 39 | name: "yOffset", 40 | type: "float", 41 | default: 0, 42 | }, 43 | { 44 | name: "blending", 45 | type: "float", 46 | default: 1, 47 | }, 48 | ], 49 | glsl: ` 50 | float lum = _luminance(_c0.rgb); 51 | vec2 pos = vec2(lum,yOffset); 52 | vec4 color = texture2D(_tex,pos); 53 | return mix(_c0,color,blending); 54 | `, 55 | }); 56 | 57 | _hydra.synth.setFunction({ 58 | name: "lookupY", 59 | type: "color", 60 | inputs: [ 61 | { 62 | name: "_tex", 63 | type: "sampler2D", 64 | default: o0, 65 | }, 66 | { 67 | name: "xOffset", 68 | type: "float", 69 | default: 0, 70 | }, 71 | { 72 | name: "blending", 73 | type: "float", 74 | default: 1, 75 | }, 76 | ], 77 | glsl: ` 78 | float lum = _luminance(_c0.rgb); 79 | vec2 pos = vec2(xOffset,lum); 80 | vec4 color = texture2D(_tex,pos); 81 | return mix(_c0,color,blending); 82 | `, 83 | }); 84 | 85 | { 86 | const hydraGradients = {}; 87 | hydraGradients.resolution = 512; 88 | 89 | function createSource() { 90 | const { constructor: HydraSource, regl, pb, width, height } = _hydra.s[0]; 91 | return new HydraSource({ regl, pb, width, height }); 92 | } 93 | 94 | window.createLinearGradient = function(angle,...args) { 95 | const cssColorFrom = (color) => { 96 | const alpha = color.pop(); 97 | const cssColor = `rgba(${color 98 | .map((cur) => Math.round(cur * 256)) 99 | .join(" ")}/ ${alpha})`; 100 | return cssColor; 101 | }; 102 | 103 | const normalizeArrayColor = (color) => { 104 | if (Array.isArray(color)) { 105 | return cssColorFrom( 106 | Array.from({ length: 4 }).map((_, i) => color[i] ?? Number(i === 3)) 107 | ); 108 | } else { 109 | return color; 110 | } 111 | }; 112 | 113 | const generateSteps = (q) => 114 | Array.from({ length: q }, (_, i) => i / (q - 1)); 115 | 116 | const generateBounds = (angle) => { 117 | const radius = hydraGradients.resolution / 2; 118 | const [s, c] = [Math.sin(angle), Math.cos(angle)]; 119 | const startX = radius - (c * radius); 120 | const startY = radius - (s * radius); 121 | const endX = radius + (c * radius); 122 | const endY = radius + (s * radius); 123 | return [startX, startY, endX, endY]; 124 | } 125 | const bounds = generateBounds(angle); 126 | const canvas = document.createElement("canvas"); 127 | canvas.width = hydraGradients.resolution; 128 | canvas.height = hydraGradients.resolution; 129 | const ctx = canvas.getContext("2d"); 130 | 131 | const hasCustomSteps = Number.isFinite(args[1]); 132 | if (hasCustomSteps && args.length % 2 !== 0) args.push(1); 133 | 134 | const filterEvery = hasCustomSteps ? 2 : 1; 135 | let [colors, steps] = [ 136 | args.filter((_, i) => i % filterEvery ^ 1).map(normalizeArrayColor), 137 | args.filter((_, i) => i % filterEvery), 138 | ]; 139 | steps = steps.length ? steps : generateSteps(colors.length); 140 | const gradient = ctx.createLinearGradient(...bounds); 141 | colors.forEach((color, i) => gradient.addColorStop(steps[i], color)); 142 | 143 | ctx.fillStyle = gradient; 144 | ctx.fillRect(0, 0, canvas.width, canvas.height); 145 | 146 | const source = createSource(); 147 | source.init({ src: canvas }, { min: "linear", mag: "linear" }); 148 | 149 | return source; 150 | } 151 | 152 | window.createGradient = (...args) => window.createLinearGradient(0,...args); 153 | } 154 | -------------------------------------------------------------------------------- /hydra-tap.js: -------------------------------------------------------------------------------- 1 | // https://github.com/bahamas10/node-bpm/blob/master/bpm.js 2 | if (typeof exports !== 'undefined') { 3 | module.exports = BPM; 4 | module.exports.BPM = BPM; 5 | } 6 | function BPM() { 7 | this.count = 0; 8 | this.ts = 0; 9 | this.old_ts = 0; 10 | } 11 | BPM.prototype.tap = function () { 12 | this.ts = Date.now(); 13 | if (!this.first_ts) this.first_ts = this.ts; 14 | var ret = {}; 15 | // ignore the first tap 16 | if (this.old_ts) { 17 | var ms = this.ts - this.old_ts; 18 | var avg = 60000 * this.count / (this.ts - this.first_ts); 19 | ret.avg = avg; 20 | ret.ms = ms; 21 | } 22 | ret.count = ++this.count; 23 | // Store the old timestamp 24 | this.old_ts = this.ts; 25 | return ret; 26 | }; 27 | BPM.prototype.reset = function () { 28 | this.count = 0; 29 | this.ts = 0; 30 | this.old_ts = 0; 31 | this.first_ts = 0; 32 | }; 33 | window.BPM = BPM; 34 | 35 | { 36 | const getHydra = function () { 37 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 38 | ? "editor" 39 | : window.atom?.packages 40 | ? "atom" 41 | : "idk"; 42 | if (whereami === "editor") { 43 | return window.hydraSynth; 44 | } 45 | if (whereami === "atom") { 46 | return global.atom.packages.loadedPackages["atom-hydra"] 47 | .mainModule.main.hydra; 48 | } 49 | let _h = [ 50 | window.hydraSynth, 51 | window._hydra, 52 | window.hydra, 53 | window.h, 54 | window.H, 55 | window.hy 56 | ].find(h => h?.regl); 57 | return _h; 58 | }; 59 | window._hydra = getHydra(); 60 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 61 | } 62 | 63 | { 64 | const tapper = new BPM(); 65 | window.hydraTap = {}; 66 | hydraTap.resyncCall = () => 0; 67 | let resetInterval = 0; 68 | 69 | document.addEventListener('keydown', function (event) { 70 | if (event.ctrlKey && event.keyCode === 32 && !(event.shiftKey)) { 71 | clearInterval(resetInterval); 72 | const tap = tapper.tap(); 73 | if (tap.count > 1) { 74 | _hydra.sandbox.set('bpm', Math.round(tap.avg)); 75 | } 76 | resetInterval = setInterval(tapper.reset.bind(tapper), 1500); 77 | } else if (event.ctrlKey && event.key === ",") { 78 | _hydra.sandbox.set('time', hydraTap.resyncCall()); 79 | } 80 | }); 81 | 82 | function setRange(f) { 83 | f.range = (min = 0, max = 1) => () => f() * (max - min) + min; 84 | return f; 85 | } 86 | function setCurve(f) { 87 | f.curve = (q = 1) => { 88 | f2 = () => q > 0 ? Math.pow(f(), q) : (1 - Math.pow(1 - f(), -q)); 89 | f2 = setRange(f2); 90 | return f2; 91 | }; 92 | return f; 93 | } 94 | function rampToFunction(ramp) { 95 | ramp = setRange(ramp); 96 | ramp = setCurve(ramp); 97 | return ramp; 98 | } 99 | 100 | const rampUp = (x) => ((time * (1 / x)) % (60 / bpm)) / (60 / bpm); 101 | const rampDown = (x) => ((60 / bpm) - ((time * (1 / x)) % (60 / bpm))) / (60 / bpm); 102 | const rampTriUp = (x) => ((time * (1 / x)) % (60 * 2 / bpm)) >= (60 / bpm) ? rampUp(x) : rampDown(x); 103 | const rampTriDown = (x) => ((time * (1 / x)) % (60 * 2 / bpm)) >= (60 / bpm) ? rampDown(x) : rampUp(x); 104 | 105 | window.beats_ = (n = 1) => rampToFunction(() => rampUp(n)) 106 | window.beats = (n = 1) => rampToFunction(() => rampDown(n)) 107 | window.beatsTri_ = (n = 1) => rampToFunction(() => rampTriUp(n)) 108 | window.beatsTri = (n = 1) => rampToFunction(() => rampTriDown(n)) 109 | 110 | // speed controls 111 | 112 | document.addEventListener('keydown', function (event) { 113 | if (event.ctrlKey && event.shiftKey && event.altKey && event.code.startsWith('Digit')) { 114 | const number = parseInt(event.code.charAt(event.code.length - 1)); 115 | const negativeSpeed = -number; 116 | _hydra.sandbox.set('speed', negativeSpeed); 117 | } else if (event.ctrlKey && event.shiftKey && event.code.startsWith('Digit')) { 118 | const number = parseInt(event.code.charAt(event.code.length - 1)); 119 | _hydra.sandbox.set('speed', number); 120 | } else if (event.ctrlKey && event.altKey && event.code.startsWith('Digit')) { 121 | const number = parseInt(event.code.charAt(event.code.length - 1)); 122 | const speed = 1 / number; 123 | _hydra.sandbox.set('speed', speed); 124 | } 125 | }); 126 | 127 | let previousSpeed = _hydra.synth.speed; 128 | document.addEventListener('keydown', function (event) { 129 | if (event.ctrlKey && event.shiftKey && event.keyCode === 32) { 130 | if (_hydra.synth.speed !== 0) { 131 | previousSpeed = _hydra.synth.speed; 132 | _hydra.sandbox.set('speed', 0); 133 | } else { 134 | _hydra.sandbox.set('speed', previousSpeed); 135 | } 136 | } 137 | }); 138 | 139 | } 140 | -------------------------------------------------------------------------------- /hydra-blend.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const blendingFuncs = { 31 | add2: "min(base+blend,1.0)", 32 | average: "(base+blend)/2.0", 33 | colorBurn: "(blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0)", 34 | colorDodge: "(blend==1.0)?blend:min(base/(1.0-blend),1.0)", 35 | darken: "min(blend,base)", 36 | difference: "abs(base-blend)", 37 | divide: "min(base/(blend+0.00000001),1.0)", 38 | exclusion: "base+blend-2.0*base*blend", 39 | glow: "(base==1.0)?base:min(blend*blend/(1.0-base),1.0)", 40 | hardLight: 41 | "blend<0.5?(2.0*blend*base):(1.0-2.0*(1.0-blend)*(1.0-base))", 42 | hardMix: 43 | "(((blend<0.5)?((blend==0.0)?(blend):max((1.0-((1.0-base)/(2.0*blend))),0.0)):(((2.0*(blend-0.5))==1.0)?(2.0*(blend-0.5)):min(base/(1.0-(2.0*(blend-0.5))),1.0)))<0.5)?0.0:1.0", 44 | lighten: "max(blend,base)", 45 | linearBurn: "max(base+blend-1.0,0.0)", 46 | linearDodge: "min(base+blend,1.0)", 47 | linearLight: 48 | "blend<0.5?(max(base+(2.0*blend)-1.0,0.0)):min(base+(2.0*(blend-0.5)),1.0)", 49 | multiply: "base*blend", 50 | negation: "1.0-abs(1.0-base-blend)", 51 | overlay: "base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend))", 52 | phoenix: "min(base,blend)-max(base,blend)+vec3(1.0)", 53 | pinLight: "(blend<0.5)?min(base,2.0*blend):max(base,2.0*(blend-0.5))", 54 | reflect: "(blend==1.0)?blend:min(base*base/(1.0-blend),1.0)", 55 | screen: "1.0-((1.0-base)*(1.0-blend))", 56 | softLight: 57 | "(blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend))", 58 | subtract: "max(base+blend-1.0,0.0)", 59 | vividLight: 60 | "(blend<0.5)?((blend==0.0)?(blend):max((1.0-((1.0-base)/(2.0*blend))),0.0)):(((2.0*(blend-0.5))==1.0)?(2.0*(blend-0.5)):min(base/(1.0-(2.0*(blend-0.5))),1.0))", 61 | }; 62 | 63 | Object.entries(blendingFuncs).forEach(([mode, def]) => { 64 | let glsl = 65 | "vec3 rgb;\n\n" + 66 | //"_c0 = clamp(_c0, 0.0, 1.0);" + 67 | //"_c1 = clamp(_c1, 0.0, 1.0);" + 68 | ["r", "g", "b"].reduce((acc, elem) => { 69 | return ( 70 | acc + 71 | (`rgb.${elem} = ` + 72 | def 73 | .replaceAll("base", "_c0." + elem) 74 | .replaceAll("blend", "_c1." + elem) + 75 | ";\n\n") 76 | ); 77 | }, "") + 78 | "_c1.a *= amount;" + 79 | "vec4 blended = vec4(mix(_c0.rgb, rgb, _c1.a), 1.0);" + 80 | "vec4 over = _c1+(_c0*(1.0-_c1.a));" + 81 | "return mix(blended, over, 1.0-_c0.a);"; 82 | _hydra.synth.setFunction({ 83 | name: mode, 84 | type: "combine", 85 | inputs: [{ name: "amount", type: "float", default: 1 }], 86 | glsl, 87 | }); 88 | }); 89 | } 90 | 91 | _hydra.synth.setFunction({ 92 | name: "layer", 93 | type: "combine", 94 | inputs: [ 95 | { 96 | type: "float", 97 | name: "amount", 98 | default: 1, 99 | }, 100 | ], 101 | glsl: ` 102 | _c0.a = clamp(_c0.a, 0.0, 1.0); 103 | _c1.a = clamp(_c1.a, 0.0, 1.0); 104 | return _c1+(_c0*(1.0-_c1.a)); 105 | `, 106 | }); 107 | _hydra.synth.setFunction({ 108 | name: "mask", 109 | type: "combine", 110 | inputs: [], 111 | glsl: ` 112 | float a = _luminance(_c1.rgb); 113 | return vec4(_c0.rgb*a, _c0.a*a); 114 | `, 115 | }); 116 | _hydra.synth.setFunction({ 117 | name: "luma", 118 | type: "color", 119 | inputs: [ 120 | { 121 | type: "float", 122 | name: "threshold", 123 | default: 0.5, 124 | }, 125 | { 126 | type: "float", 127 | name: "tolerance", 128 | default: 0.1, 129 | }, 130 | ], 131 | glsl: ` 132 | float a = smoothstep(threshold-(tolerance+0.0000001), threshold+(tolerance+0.0000001), _luminance(_c0.rgb*_c0.a)); 133 | return vec4(_c0.rgb*a, a); 134 | `, 135 | }); 136 | _hydra.synth.setFunction({ 137 | name: "clamp", 138 | type: "color", 139 | inputs: [], 140 | glsl: ` 141 | return clamp(_c0,0.0,1.0); 142 | `, 143 | }); 144 | _hydra.synth.setFunction({ 145 | name: "premultiply", 146 | type: "color", 147 | inputs: [], 148 | glsl: ` 149 | _c0 = clamp(_c0,0.0,1.0); 150 | return vec4(_c0.rgb*_c0.a,_c0.a); 151 | `, 152 | }); 153 | 154 | { 155 | const gS = _hydraScope.osc().constructor.prototype; 156 | 157 | gS.pm = gS.premultiply; 158 | 159 | gS.negate = gS.negation; 160 | } 161 | -------------------------------------------------------------------------------- /hydra-src.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const canvas = _hydra.canvas; 31 | const scope = (_hydraScope.srcMask = function (tex) { 32 | return _hydraScope.src(tex).mask(shape(4, 1, 0)); 33 | }); 34 | _hydraScope.srcAbs = function (tex) { 35 | if (!tex.hasOwnProperty("src")) return src(tex); 36 | const w = () => tex.src?.width || tex.src?.videoWidth || 0; 37 | const h = () => tex.src?.height || tex.src?.videoHeight || 0; 38 | return _hydraScope.src(tex).scale( 39 | 1, 40 | () => w() / canvas.clientWidth, 41 | () => h() / canvas.clientHeight 42 | ); 43 | }; 44 | _hydraScope.srcAbsMask = function (tex) { 45 | if (!tex.hasOwnProperty("src")) return src(tex); 46 | const w = () => tex.src?.width || tex.src?.videoWidth || 0; 47 | const h = () => tex.src?.height || tex.src?.videoHeight || 0; 48 | return _hydraScope.srcMask(tex).scale( 49 | 1, 50 | () => w() / canvas.clientWidth, 51 | () => h() / canvas.clientHeight 52 | ); 53 | }; 54 | _hydraScope.srcRel = function (tex) { 55 | if (!tex.hasOwnProperty("src")) return src(tex); 56 | const w = () => 57 | tex.src?.width 58 | ? tex.src?.width / tex.src?.height 59 | : tex.src?.videoWidth 60 | ? tex.src?.videoWidth / tex.src?.videoHeight 61 | : 0; 62 | const h = () => 63 | tex.src?.height 64 | ? tex.src?.height / tex.src?.width 65 | : tex.src?.videoHeight 66 | ? tex.src?.videoHeight / tex.src?.videoWidth 67 | : 0; 68 | const cw = () => canvas.clientWidth / canvas.clientHeight; 69 | const ch = () => canvas.clientHeight / canvas.clientWidth; 70 | return _hydraScope.src(tex).scale( 71 | 1, 72 | () => { 73 | const _cw = cw(); 74 | const _w = w(); 75 | return _cw > _w ? _w / _cw : 1; 76 | }, 77 | () => { 78 | const _ch = ch(); 79 | const _h = h(); 80 | return _ch > _h ? _h / _ch : 1; 81 | } 82 | ); 83 | }; 84 | _hydraScope.srcRelMask = function (tex) { 85 | if (!tex.hasOwnProperty("src")) return src(tex); 86 | const w = () => 87 | tex.src?.width 88 | ? tex.src?.width / tex.src?.height 89 | : tex.src?.videoWidth 90 | ? tex.src?.videoWidth / tex.src?.videoHeight 91 | : 0; 92 | const h = () => 93 | tex.src?.height 94 | ? tex.src?.height / tex.src?.width 95 | : tex.src?.videoHeight 96 | ? tex.src?.videoHeight / tex.src?.videoWidth 97 | : 0; 98 | const cw = () => canvas.clientWidth / canvas.clientHeight; 99 | const ch = () => canvas.clientHeight / canvas.clientWidth; 100 | return _hydraScope.srcMask(tex).scale( 101 | 1, 102 | () => { 103 | const _cw = cw(); 104 | const _w = w(); 105 | return _cw > _w ? _w / _cw : 1; 106 | }, 107 | () => { 108 | const _ch = ch(); 109 | const _h = h(); 110 | return _ch > _h ? _h / _ch : 1; 111 | } 112 | ); 113 | }; 114 | 115 | const gS = _hydraScope.osc().constructor.prototype; 116 | gS.rotateRel = function(...args){ 117 | return this .scale(1,canvas.clientWidth/canvas.clientHeight) 118 | .rotate(...args) 119 | .scale(1,canvas.clientHeight/canvas.clientWidth) 120 | } 121 | gS.correctScaleRel = function(source){ 122 | const tex = source; 123 | if (!tex.hasOwnProperty("src")) return src(tex); 124 | const w = () => 125 | tex.src?.width 126 | ? tex.src?.width / tex.src?.height 127 | : tex.src?.videoWidth 128 | ? tex.src?.videoWidth / tex.src?.videoHeight 129 | : 0; 130 | const h = () => 131 | tex.src?.height 132 | ? tex.src?.height / tex.src?.width 133 | : tex.src?.videoHeight 134 | ? tex.src?.videoHeight / tex.src?.videoWidth 135 | : 0; 136 | const cw = () => canvas.clientWidth / canvas.clientHeight; 137 | const ch = () => canvas.clientHeight / canvas.clientWidth; 138 | return this.scale( 139 | 1, 140 | () => { 141 | const _cw = cw(); 142 | const _w = w(); 143 | return _cw > _w ? _w / _cw : 1; 144 | }, 145 | () => { 146 | const _ch = ch(); 147 | const _h = h(); 148 | return _ch > _h ? _h / _ch : 1; 149 | } 150 | ); 151 | } 152 | gS.noWrap = function(sm=0){ 153 | return this.mask(shape(4, 1, sm)); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /hydra-text.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | _hydraScope.srcRelMask = function (tex) { 30 | if (!tex.hasOwnProperty("src")) return src(tex); 31 | const w = () => 32 | tex.src?.width 33 | ? tex.src?.width / tex.src?.height 34 | : tex.src?.videoWidth 35 | ? tex.src?.videoWidth / tex.src?.videoHeight 36 | : 0; 37 | const h = () => 38 | tex.src?.height 39 | ? tex.src?.height / tex.src?.width 40 | : tex.src?.videoHeight 41 | ? tex.src?.videoHeight / tex.src?.videoWidth 42 | : 0; 43 | const cw = () => _hydra.canvas.clientWidth / _hydra.canvas.clientHeight; 44 | const ch = () => _hydra.canvas.clientHeight / _hydra.canvas.clientWidth; 45 | return _hydraScope 46 | .src(tex) 47 | .mask(shape(4, 1, 0)) 48 | .scale( 49 | 1, 50 | () => { 51 | const _cw = cw(); 52 | const _w = w(); 53 | return _cw > _w ? _w / _cw : 1; 54 | }, 55 | () => { 56 | const _ch = ch(); 57 | const _h = h(); 58 | return _ch > _h ? _h / _ch : 1; 59 | } 60 | ); 61 | }; 62 | 63 | { 64 | const Source = _hydra.s[0].constructor; 65 | window.hydraText = { 66 | font: "sans-serif", 67 | fontStyle: "normal", 68 | fontSize: "auto", 69 | textAlign: "center", 70 | fillStyle: "white", 71 | strokeStyle: "white", 72 | lineWidth: "2%", 73 | lineJoin: "miter", 74 | canvasResize: 2, 75 | interpolation: "linear" 76 | }; 77 | 78 | function createSource() { 79 | const s = new Source({ 80 | regl: _hydra.regl, 81 | pb: _hydra.pb, 82 | width: _hydra.width, 83 | height: _hydra.height, 84 | }); 85 | return s; 86 | } 87 | function isPercentage(property){ 88 | return String(property).endsWith("%"); 89 | } 90 | function getPercentage(property){ 91 | return Number(property.substring(0,property.length-1)) / 100; 92 | } 93 | const _text = function (str, _config, fill = true, stroke = false, fillAfter = false) { 94 | const s = createSource(); 95 | const canvas = document.createElement("canvas"); 96 | const ctx = canvas.getContext("2d"); 97 | const lines = str.split("\n"); 98 | const longestLine = lines.reduce((a,b) => a.length > b.length ? a : b ); 99 | 100 | if (typeof _config == "string") _config = { font: _config }; 101 | 102 | const config = Object.assign({}, hydraText); 103 | Object.assign(config, _config); 104 | const font = config.font; 105 | config.font = undefined; 106 | const fontStyle = config.fontStyle; 107 | config.fontStyle = undefined; 108 | config.textBaseline = "middle"; 109 | 110 | const fontWithSize = (size) => `${fontStyle} ${size} ${font}`; 111 | 112 | Object.assign(ctx, config); 113 | 114 | canvas.width = _hydra.width; 115 | ctx.font = fontWithSize("1px"); 116 | let padding = _hydra.width / 20; 117 | let textWidth = _hydra.width - padding; 118 | let fontSize = textWidth / ctx.measureText(longestLine).width; 119 | canvas.height = fontSize * 1.4 * lines.length; 120 | 121 | if(isPercentage(config.fontSize)) fontSize *= getPercentage(config.fontSize); 122 | else if (config.fontSize != "auto") fontSize = Number(config.fontSize.replace(/[^0-9.,]+/, '')); 123 | if(isPercentage(config.lineWidth)) config.lineWidth = fontSize * getPercentage(config.lineWidth); 124 | config.fontSize = undefined; 125 | 126 | fontSize *= config.canvasResize; 127 | canvas.width *= config.canvasResize; 128 | canvas.height *= config.canvasResize; 129 | textWidth *= config.canvasResize; 130 | padding *= config.canvasResize; 131 | config.lineWidth *= config.canvasResize; 132 | 133 | config.font = fontWithSize(String(fontSize) + "px"); 134 | Object.assign(ctx, config); 135 | 136 | let x = 0; 137 | if (ctx.textAlign == "center") x = canvas.width / 2; 138 | else if (ctx.textAlign == "left") x = padding / 2; 139 | else if (ctx.textAlign == "right") x = canvas.width - padding / 2; 140 | 141 | lines.forEach((line, i) => { 142 | const y = (canvas.height / (lines.length + 1)) * (i + 1); 143 | if (fill) ctx.fillText(line, x, y, textWidth); 144 | if (stroke) ctx.strokeText(line, x, y, textWidth); 145 | if (fillAfter) ctx.fillText(line, x, y, textWidth); 146 | }); 147 | 148 | s.init({ src: canvas }, { min: config.interpolation, mag: config.interpolation }); 149 | 150 | return _hydraScope.srcRelMask(s); 151 | }; 152 | _hydraScope.text = function (str, config) { 153 | return _text(str, config); 154 | }; 155 | _hydraScope.strokeText = function (str, config) { 156 | return _text(str, config, false, true); 157 | }; 158 | _hydraScope.fillStrokeText = function (str, config) { 159 | return _text(str, config, true, true, false); 160 | }; 161 | _hydraScope.strokeFillText = function (str, config) { 162 | return _text(str, config, false, true, true); 163 | }; 164 | } 165 | -------------------------------------------------------------------------------- /hydra-debug.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | async function main() { 31 | await _hydraScope.loadScript( 32 | "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js" 33 | ); 34 | await _hydraScope.loadScript( 35 | "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/glsl.min.js" 36 | ); 37 | await _hydraScope.loadScript( 38 | "https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.7/beautify.js" 39 | ); 40 | 41 | hljs.configure({ ignoreUnescapedHTML: true }); 42 | const brPlugin = { 43 | "before:highlightBlock": ({ block }) => { 44 | block.innerHTML = block.innerHTML 45 | .replace(/\n/g, "") 46 | .replace(//g, "\n"); 47 | }, 48 | "after:highlightBlock": ({ result }) => { 49 | result.value = result.value.replace(/\n/g, "
"); 50 | }, 51 | }; 52 | hljs.addPlugin(brPlugin); 53 | 54 | const link = document.createElement("link"); 55 | link.href = 56 | "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/base16/tomorrow-night.min.css"; 57 | link.type = "text/css"; 58 | link.rel = "stylesheet"; 59 | document.getElementsByTagName("head")[0].appendChild(link); 60 | 61 | { 62 | function cleanCode(code) { 63 | return js_beautify(code).replace(/\n{2,}/gm, "\n\n") + "\n\n"; 64 | } 65 | function logHighlightedCode(glslSource, output) { 66 | let passes = glslSource.glsl(); 67 | let code = passes[0].frag; 68 | 69 | document.getElementById("hydra-debug")?.remove(); 70 | 71 | const pre = document.createElement("pre"); 72 | pre.className = "hljs"; 73 | pre.style.position = "sticky"; 74 | pre.style.height = "96%"; 75 | pre.style.padding = "4%"; 76 | pre.style.overflow = "scroll"; 77 | const codeElement = document.createElement("code"); 78 | codeElement.className = "language-glsl"; 79 | pre.appendChild(codeElement); 80 | codeElement.innerText = cleanCode(code) 81 | .replace(/&/g, "&") 82 | .replace(//g, ">") 84 | .replace(/"/g, """) 85 | .replace(/'/g, "'"); 86 | hljs.highlightElement(pre, { 87 | language: "glsl", 88 | ignoreIllegals: true, 89 | }); 90 | 91 | const wrapper = document.createElement("div"); 92 | wrapper.id = "hydra-debug"; 93 | wrapper.style.zIndex = 999; 94 | wrapper.style.overflow = "hidden"; 95 | wrapper.style.position = "fixed"; 96 | wrapper.style.width = "40%"; 97 | wrapper.style.height = "90%"; 98 | wrapper.style.left = "58%"; 99 | wrapper.style.top = "5%"; 100 | wrapper.style.fontSize = "14px"; 101 | 102 | const close = document.createElement("button"); 103 | close.innerText = "x"; 104 | close.style.position = "absolute"; 105 | close.style.right = "0px"; 106 | close.style.top = "2%"; 107 | close.style.fontSize = "20px"; 108 | close.style.backgroundColor = "white"; 109 | close.style.color = "black"; 110 | close.style.border = "none"; 111 | close.onclick = () => { 112 | document.getElementById("hydra-debug")?.remove(); 113 | }; 114 | 115 | wrapper.appendChild(pre); 116 | wrapper.appendChild(close); 117 | 118 | if (output) { 119 | pre.contentEditable = "true"; 120 | const run = document.createElement("button"); 121 | run.innerText = ">"; 122 | run.style.position = "absolute"; 123 | run.style.right = "30px"; 124 | run.style.top = "2%"; 125 | run.style.fontSize = "20px"; 126 | run.style.backgroundColor = "white"; 127 | run.style.color = "black"; 128 | run.style.border = "none"; 129 | run.onclick = () => { 130 | codeElement.innerText = cleanCode(pre.innerText) 131 | pre.innerHTML = ""; 132 | pre.appendChild(codeElement); 133 | hljs.highlightElement(pre, { 134 | language: "glsl", 135 | ignoreIllegals: true, 136 | }); 137 | passes[0].frag = pre.innerText 138 | .replace("&", "&") 139 | .replace("<", "<") 140 | .replace(">", ">") 141 | .replace(""", '"') 142 | .replace("'", "'"); 143 | output.render(passes); 144 | }; 145 | wrapper.appendChild(run); 146 | } 147 | 148 | document.body.append(wrapper); 149 | } 150 | 151 | const gS = _hydraScope.osc().constructor.prototype; 152 | gS.debug = function (output) { 153 | logHighlightedCode(this, output); 154 | return output ? this.out(output) : this; 155 | }; 156 | } 157 | } 158 | 159 | main(); 160 | } 161 | -------------------------------------------------------------------------------- /hydra-glsl.js: -------------------------------------------------------------------------------- 1 | //hydra-gsls 2 | //code glsl on the fly inside Hydra code 3 | 4 | { 5 | const getHydra = function () { 6 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 7 | ? "editor" 8 | : window.atom?.packages 9 | ? "atom" 10 | : "idk"; 11 | if (whereami === "editor") { 12 | return window.hydraSynth; 13 | } 14 | if (whereami === "atom") { 15 | return global.atom.packages.loadedPackages["atom-hydra"] 16 | .mainModule.main.hydra; 17 | } 18 | let _h = [ 19 | window.hydraSynth, 20 | window._hydra, 21 | window.hydra, 22 | window.h, 23 | window.H, 24 | window.hy 25 | ].find(h => h?.regl); 26 | return _h; 27 | }; 28 | window._hydra = getHydra(); 29 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 30 | } 31 | 32 | { 33 | const gS = _hydraScope.osc().constructor.prototype; 34 | const _glslExtension = { 35 | inputArray: () => 36 | new Array(10) 37 | .fill("i") 38 | .map((x, i) => ({ name: x + i, type: "float", default: 1 })), 39 | nodeCount: 0, 40 | checkCode: function (code) { 41 | code = code.trim(); 42 | let lines = code.split(";"); 43 | let ll = lines.length - 1; 44 | if (!lines[ll]) { 45 | lines.pop(); 46 | ll--; 47 | } 48 | lines[ll] = lines[ll].trim(); 49 | lines[ll] = 50 | "\n" + 51 | (lines[ll].substring(0, 6) != "return" 52 | ? "return " + lines[ll] 53 | : lines[ll]) + 54 | ";"; 55 | code = lines.join(";"); 56 | return code; 57 | }, 58 | getObjAndArgs: function (type, args) { 59 | let inputArray = false; 60 | if (args[0] instanceof Array && args[0][0].constructor === String) { 61 | inputArray = this.inputArray().map((x, i) => { 62 | x.name = args[i] ? args[i][0] : x.name; 63 | return x; 64 | }); 65 | inputArray = inputArray.slice(0, args.length); 66 | args = args.map((x) => x[1]); 67 | } 68 | let obj = { 69 | name: "_glsl_ext_NODE_" + this.nodeCount, 70 | type: type, 71 | inputs: inputArray || this.inputArray(), 72 | }; 73 | this.nodeCount++; 74 | return [obj, args]; 75 | }, 76 | glslSource: function (code, ...args) { 77 | let prefix = [ 78 | !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", 79 | !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", 80 | !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", 81 | ].join(""); 82 | let data = this.getObjAndArgs("src", args); 83 | let obj = data[0]; 84 | args = data[1]; 85 | obj.glsl = prefix + this.checkCode(code); 86 | _hydra.synth.setFunction(obj); 87 | return globalThis[obj.name](...args); 88 | }, 89 | glslColor: function (self, code, ...args) { 90 | let prefix = [ 91 | !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", 92 | !code.includes("vec4 color") ? "vec4 color = _c0;\n" : "", 93 | ].join(""); 94 | let data = this.getObjAndArgs("color", args); 95 | let obj = data[0]; 96 | args = data[1]; 97 | obj.glsl = prefix + this.checkCode(code); 98 | _hydra.synth.setFunction(obj); 99 | return gS[obj.name].bind(self)(...args); 100 | }, 101 | glslHsv: function (self, code, ...args) { 102 | code = "vec3 hsv = _rgbToHsv(c0.rgb);\n" + code; 103 | code = code + "\nreturn vec4(_hsvToRgb(hsv),c0.a);"; 104 | return this.glslColor(self, code, ...args); 105 | }, 106 | glslCoord: function (self, code, ...args) { 107 | let prefix = [ 108 | !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", 109 | !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", 110 | !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", 111 | ].join(""); 112 | let data = this.getObjAndArgs("coord", args); 113 | let obj = data[0]; 114 | args = data[1]; 115 | obj.glsl = prefix + this.checkCode(code); 116 | _hydra.synth.setFunction(obj); 117 | return gS[obj.name].bind(self)(...args); 118 | }, 119 | glslCombine: function (self, code, texture, ...args) { 120 | let prefix = [ 121 | !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", 122 | !code.includes("vec4 color0") ? "vec4 color0 = _c0;\n" : "", 123 | !code.includes("vec4 c1") ? "vec4 c1 = _c1;\n" : "", 124 | !code.includes("vec4 color1") ? "vec4 color1 = _c1;\n" : "", 125 | ].join(""); 126 | let data = this.getObjAndArgs("combine", args); 127 | let obj = data[0]; 128 | args = data[1]; 129 | args.unshift(texture); 130 | obj.glsl = prefix + this.checkCode(code); 131 | _hydra.synth.setFunction(obj); 132 | return gS[obj.name].bind(self)(...args); 133 | }, 134 | glslCombineCoord: function (self, code, texture, ...args) { 135 | let prefix = [ 136 | !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", 137 | !code.includes("vec4 color") ? "vec4 color = _c0;\n" : "", 138 | !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", 139 | !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", 140 | !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", 141 | ].join(""); 142 | let data = this.getObjAndArgs("combineCoord", args); 143 | let obj = data[0]; 144 | args = data[1]; 145 | args.unshift(texture); 146 | obj.glsl = prefix + this.checkCode(code); 147 | _hydra.synth.setFunction(obj); 148 | return gS[obj.name].bind(self)(...args); 149 | }, 150 | }; 151 | 152 | _hydraScope.glsl = _glslExtension.glslSource.bind(_glslExtension); 153 | gS.glslColor = function (code, ...args) { 154 | return _glslExtension.glslColor(this, code, ...args); 155 | }; 156 | gS.glslHsv = function (code, ...args) { 157 | return _glslExtension.glslHsv(this, code, ...args); 158 | }; 159 | gS.glslCoord = function (code, ...args) { 160 | return _glslExtension.glslCoord(this, code, ...args); 161 | }; 162 | gS.glslCombine = function (code, texture, ...args) { 163 | return _glslExtension.glslCombine(this, code, texture, ...args); 164 | }; 165 | gS.glslBlend = gS.glslCombine; 166 | gS.glslCombineCoord = function (code, texture, ...args) { 167 | return _glslExtension.glslCombineCoord(this, code, texture, ...args); 168 | }; 169 | gS.glslModulate = gS.glslCombineCoord; 170 | } 171 | -------------------------------------------------------------------------------- /hydra-wrap.js: -------------------------------------------------------------------------------- 1 | // prereq 2 | 3 | //reset fbos. make sure they use clamp 4 | //code taken from hydra-outputs.js 5 | 6 | { 7 | const getHydra = function () { 8 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 9 | ? "editor" 10 | : window.atom?.packages 11 | ? "atom" 12 | : "idk"; 13 | if (whereami === "editor") { 14 | return window.hydraSynth; 15 | } 16 | if (whereami === "atom") { 17 | return global.atom.packages.loadedPackages["atom-hydra"] 18 | .mainModule.main.hydra; 19 | } 20 | let _h = [ 21 | window.hydraSynth, 22 | window._hydra, 23 | window.hydra, 24 | window.h, 25 | window.H, 26 | window.hy 27 | ].find(h => h?.regl); 28 | return _h; 29 | }; 30 | window._hydra = getHydra(); 31 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 32 | } 33 | 34 | { 35 | if (!_hydraScope.oS) { 36 | const oP = _hydra.o[0].constructor.prototype; 37 | oP.fboSettings = Array(2).fill({ 38 | mag: "nearest", 39 | min: "nearest", 40 | width: width, 41 | height: height, 42 | format: "rgba", 43 | }); 44 | oP.setFbos = function (fbo0, fbo1) { 45 | var colors = fbo1 ? [fbo0, fbo1] : [fbo0, fbo0]; 46 | this.fboSettings = colors.map((x, i) => { 47 | return { 48 | ...this.fboSettings[i], 49 | width: width, 50 | height: height, 51 | ...x, 52 | }; 53 | }); 54 | this.fbos = this.fboSettings.map((x) => 55 | this.regl.framebuffer({ 56 | color: this.regl.texture(x), 57 | depthStencil: false, 58 | }) 59 | ); 60 | }; 61 | oP.setClamp = function () { 62 | this.setFbos({ wrapS: "clamp", wrapT: "clamp" }); 63 | }; 64 | _hydraScope.oS = { outputs: window._hydra.o }; 65 | _hydraScope.oS.setClamp = function () { 66 | this.outputs.forEach((x) => x.setClamp()); 67 | }; 68 | } 69 | _hydraScope.oS.setClamp(); 70 | } 71 | 72 | // set all coord functions to no-wrap 73 | [ 74 | { 75 | name: "scroll", 76 | type: "coord", 77 | inputs: [ 78 | { 79 | type: "float", 80 | name: "scrollX", 81 | default: 0.5, 82 | }, 83 | { 84 | type: "float", 85 | name: "scrollY", 86 | default: 0.5, 87 | }, 88 | { 89 | type: "float", 90 | name: "speedX", 91 | default: 0, 92 | }, 93 | { 94 | type: "float", 95 | name: "speedY", 96 | default: 0, 97 | }, 98 | ], 99 | glsl: `_st.x += scrollX + time*speedX; _st.y += scrollY + time*speedY; return _st;`, 100 | }, 101 | { 102 | name: "scrollX", 103 | type: "coord", 104 | inputs: [ 105 | { 106 | type: "float", 107 | name: "scrollX", 108 | default: 0.5, 109 | }, 110 | { 111 | type: "float", 112 | name: "speed", 113 | default: 0, 114 | }, 115 | ], 116 | glsl: `_st.x += scrollX + time*speed; return _st;`, 117 | }, 118 | { 119 | name: "modulateScrollX", 120 | type: "combineCoord", 121 | inputs: [ 122 | { 123 | type: "float", 124 | name: "scrollX", 125 | default: 0.5, 126 | }, 127 | { 128 | type: "float", 129 | name: "speed", 130 | default: 0, 131 | }, 132 | ], 133 | glsl: `_st.x += _c0.r*scrollX + time*speed; return _st;`, 134 | }, 135 | { 136 | name: "scrollY", 137 | type: "coord", 138 | inputs: [ 139 | { 140 | type: "float", 141 | name: "scrollY", 142 | default: 0.5, 143 | }, 144 | { 145 | type: "float", 146 | name: "speed", 147 | default: 0, 148 | }, 149 | ], 150 | glsl: `_st.y += scrollY + time*speed; return _st;`, 151 | }, 152 | { 153 | name: "modulateScrollY", 154 | type: "combineCoord", 155 | inputs: [ 156 | { 157 | type: "float", 158 | name: "scrollY", 159 | default: 0.5, 160 | }, 161 | { 162 | type: "float", 163 | name: "speed", 164 | default: 0, 165 | }, 166 | ], 167 | glsl: ` _st.y += _c0.r*scrollY + time*speed; return _st;`, 168 | }, 169 | ].forEach((x) => _hydra.synth.setFunction(x)); 170 | 171 | // hydraWrap 172 | 173 | window.hydraWrap = {}; 174 | 175 | hydraWrap.defaultList = [ 176 | { 177 | name: "prev", 178 | type: "src", 179 | inputs: [], 180 | glsl: ` vec4 c0 = texture2D(prevBuffer, wrap(_st)); 181 | //c0 *= step(abs(_st.x-0.5),0.5); 182 | //c0 *= step(abs(_st.t-0.5),0.5); 183 | return c0;`, 184 | }, 185 | { 186 | name: "src", 187 | type: "src", 188 | inputs: [ 189 | { 190 | type: "sampler2D", 191 | name: "tex", 192 | default: NaN, 193 | }, 194 | ], 195 | glsl: ` vec4 c0 = texture2D(tex, wrap(_st)); 196 | //c0 *= step(abs(_st.x-0.5),0.5); 197 | //c0 *= step(abs(_st.t-0.5),0.5); 198 | return c0;`, 199 | }, 200 | { 201 | name: "wrap", 202 | type: "coord", 203 | inputs: [], 204 | glsl: `return wrap(_st);`, 205 | }, 206 | ]; 207 | 208 | hydraWrap.void = false; 209 | 210 | hydraWrap.generateFunctionListFromWrapper = function (wrapper) { 211 | return Array.from(hydraWrap.defaultList).map((_f) => { 212 | let f = Object.assign({}, _f); 213 | f.glsl = f.glsl.replace("wrap(_st)", wrapper); 214 | f.glsl = hydraWrap.void ? f.glsl.replaceAll("//c0", "c0") : f.glsl; 215 | return f; 216 | }); 217 | }; 218 | 219 | hydraWrap.wrappers = { 220 | wrap: "fract(_st)", 221 | nowrap: "_st", 222 | mirror: "-abs(fract(_st/2.0)*2.0-1.0)+1.0", 223 | }; 224 | 225 | hydraWrap.currentWrapper = hydraWrap.wrappers.wrap; 226 | 227 | hydraWrap.setWrap = function () { 228 | hydraWrap.void = false; 229 | hydraWrap.currentWrapper = hydraWrap.wrappers.wrap; 230 | hydraWrap 231 | .generateFunctionListFromWrapper(hydraWrap.wrappers.wrap) 232 | .forEach((x) => _hydra.synth.setFunction(x)); 233 | }; 234 | 235 | hydraWrap.setRepeat = hydraWrap.setWrap; 236 | 237 | hydraWrap.setNoWrap = function () { 238 | hydraWrap.void = false; 239 | hydraWrap.currentWrapper = hydraWrap.wrappers.nowrap; 240 | hydraWrap 241 | .generateFunctionListFromWrapper(hydraWrap.wrappers.nowrap) 242 | .forEach((x) => _hydra.synth.setFunction(x)); 243 | }; 244 | 245 | hydraWrap.setClamp = hydraWrap.setNoWrap; 246 | 247 | hydraWrap.setMirror = function () { 248 | hydraWrap.void = false; 249 | hydraWrap.currentWrapper = hydraWrap.wrappers.mirror; 250 | hydraWrap 251 | .generateFunctionListFromWrapper(hydraWrap.wrappers.mirror) 252 | .forEach((x) => _hydra.synth.setFunction(x)); 253 | }; 254 | 255 | hydraWrap.setCustom = function (wrapper = "_st") { 256 | hydraWrap.void = false; 257 | hydraWrap.currentWrapper = wrapper; 258 | hydraWrap 259 | .generateFunctionListFromWrapper(wrapper) 260 | .forEach((x) => _hydra.synth.setFunction(x)); 261 | }; 262 | 263 | // setVoid should only be called after setting a wrapping mode 264 | hydraWrap.setVoid = function (to = true) { 265 | hydraWrap.void = to; 266 | hydraWrap 267 | .generateFunctionListFromWrapper(hydraWrap.currentWrapper) 268 | .forEach((x) => _hydra.synth.setFunction(x)); 269 | }; 270 | 271 | hydraWrap.setWrap(); 272 | -------------------------------------------------------------------------------- /hydra-arithmetics.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | const gS = _hydraScope.osc().constructor.prototype; 31 | 32 | function wrapColorCombine(name, color, combine) { 33 | gS[name] = function (arg, ...args) { 34 | let isArgument = 35 | typeof arg == "number" || 36 | typeof arg == "function" || 37 | typeof arg == "string" || 38 | arg.constructor.name == "Array"; 39 | return isArgument 40 | ? this[color](arg) 41 | : args.length 42 | ? this[combine](arg, ...args) 43 | : this[combine](arg); 44 | }; 45 | } 46 | 47 | { 48 | const noInputs = ( 49 | "abs,sign,fract," + 50 | "sin,cos,tan,asin,acos,atan," + 51 | "exp,log,exp2,log2,sqrt,inversesqrt" 52 | ).split(","); 53 | 54 | noInputs.forEach((name) => { 55 | _hydra.synth.setFunction({ 56 | name: "_" + name, 57 | type: "color", 58 | inputs: [], 59 | glsl: `return ${name}(_c0);`, 60 | }); 61 | gS[name] = gS["_" + name]; 62 | }); 63 | } 64 | 65 | { 66 | const singleArgument = "mod,min,max,step,pow".split(","); 67 | 68 | singleArgument.forEach((name) => { 69 | _hydra.synth.setFunction({ 70 | name: "_" + name, 71 | type: "combine", 72 | inputs: [], 73 | glsl: `return ${name}(_c0,_c1);`, 74 | }); 75 | _hydra.synth.setFunction({ 76 | name: "_" + name + "_single", 77 | type: "color", 78 | inputs: [{ name: "_in", type: "float", default: 1 }], 79 | glsl: `return ${name}(_c0,vec4(_in));`, 80 | }); 81 | wrapColorCombine(name, "_" + name + "_single", "_" + name); 82 | }); 83 | } 84 | 85 | _hydra.synth.setFunction({ 86 | name: "_div", 87 | type: "combine", 88 | inputs: [], 89 | glsl: `return _c0 / _c1;`, 90 | }); 91 | _hydra.synth.setFunction({ 92 | name: "_div_single", 93 | type: "color", 94 | inputs: [{ name: "_in", type: "float", default: 1 }], 95 | glsl: `return _c0 / _in;`, 96 | }); 97 | wrapColorCombine("div", "_div_single", "_div"); 98 | 99 | _hydra.synth.setFunction({ 100 | name: "_add", 101 | type: "combine", 102 | inputs: [{ type: "float", name: "amount", default: 1 }], 103 | glsl: `return _c0 + (_c1*amount);`, 104 | }); 105 | _hydra.synth.setFunction({ 106 | name: "_add_single", 107 | type: "color", 108 | inputs: [{ name: "_in", type: "float", default: 1 }], 109 | glsl: `return _c0 + _in;`, 110 | }); 111 | wrapColorCombine("add", "_add_single", "_add"); 112 | 113 | _hydra.synth.setFunction({ 114 | name: "_sub", 115 | type: "combine", 116 | inputs: [{ type: "float", name: "amount", default: 1 }], 117 | glsl: `return _c0 - (_c1*amount);`, 118 | }); 119 | _hydra.synth.setFunction({ 120 | name: "_sub_single", 121 | type: "color", 122 | inputs: [{ name: "_in", type: "float", default: 1 }], 123 | glsl: `return _c0 - _in;`, 124 | }); 125 | wrapColorCombine("sub", "_sub_single", "_sub"); 126 | 127 | _hydra.synth.setFunction({ 128 | name: "_mult", 129 | type: "combine", 130 | inputs: [{ type: "float", name: "amount", default: 1 }], 131 | glsl: `return _c0*(1.0-amount)+(_c0*_c1)*amount;`, 132 | }); 133 | _hydra.synth.setFunction({ 134 | name: "_mult_single", 135 | type: "color", 136 | inputs: [{ name: "_in", type: "float", default: 1 }], 137 | glsl: `return _c0 * _in;`, 138 | }); 139 | wrapColorCombine("mult", "_mult_single", "_mult"); 140 | 141 | gS.amp = gS._mult_single; 142 | gS.amplitude = gS.amp; 143 | gS.offset = gS._add_single; 144 | gS.off = gS.offset; 145 | 146 | _hydra.synth.setFunction({ 147 | name: "bipolar", 148 | type: "color", 149 | inputs: [{ name: "amp", type: "float", default: 1 }], 150 | glsl: `return ((_c0 * 2.0) - 1.0) * amp;`, 151 | }); 152 | _hydra.synth.setFunction({ 153 | name: "unipolar", 154 | type: "color", 155 | inputs: [{ name: "amp", type: "float", default: 1 }], 156 | glsl: `return ((_c0 + 1.0) * 0.5) * amp;`, 157 | }); 158 | 159 | _hydra.synth.setFunction({ 160 | name: "range", 161 | type: "color", 162 | inputs: [ 163 | { name: "_min", type: "float", default: 0 }, 164 | { name: "_max", type: "float", default: 1 }, 165 | ], 166 | glsl: `return _c0 * (_max - _min) + _min;`, 167 | }); 168 | _hydra.synth.setFunction({ 169 | name: "birange", 170 | type: "color", 171 | inputs: [ 172 | { name: "_min", type: "float", default: 0 }, 173 | { name: "_max", type: "float", default: 1 }, 174 | ], 175 | glsl: `return ((_c0 + 1.0) * 0.5) * (_max - _min) + _min;`, 176 | }); 177 | _hydra.synth.setFunction({ 178 | name: "_clamp", 179 | type: "color", 180 | inputs: [ 181 | { name: "_min", type: "float", default: 0 }, 182 | { name: "_max", type: "float", default: 1 }, 183 | ], 184 | glsl: `return clamp(_c0, _min, _max);`, 185 | }); 186 | gS.clamp = gS._clamp; 187 | 188 | _hydra.synth.setFunction({ 189 | name: "x", 190 | type: "src", 191 | inputs: [ 192 | { name: "mult", type: "float", default: 1 }, 193 | ], 194 | glsl: `return vec4(vec3(_st.x*mult),1.0);`, 195 | }); 196 | _hydra.synth.setFunction({ 197 | name: "y", 198 | type: "src", 199 | inputs: [ 200 | { name: "mult", type: "float", default: 1 }, 201 | ], 202 | glsl: `return vec4(vec3(_st.y*mult),1.0);`, 203 | }); 204 | _hydra.synth.setFunction({ 205 | name: "length", 206 | type: "src", 207 | inputs: [ 208 | { name: "mult", type: "float", default: 1 }, 209 | ], 210 | glsl: `return vec4(vec3(length(_st*mult)),1.0);`, 211 | }); 212 | _hydra.synth.setFunction({ 213 | name: "distance", 214 | type: "src", 215 | inputs: [ 216 | { name: "px", type: "float", default: 0 }, 217 | { name: "py", type: "float", default: 0 }, 218 | ], 219 | glsl: `return vec4(vec3(length(_st,vec2(px,py))),1.0);`, 220 | }); 221 | 222 | _hydra.synth.setFunction({ 223 | name: "xCenter", 224 | type: "src", 225 | inputs: [ 226 | { name: "mult", type: "float", default: 1 }, 227 | ], 228 | glsl: `return vec4(vec3((0.5-_st.x)*mult),1.0);`, 229 | }); 230 | _hydra.synth.setFunction({ 231 | name: "yCenter", 232 | type: "src", 233 | inputs: [ 234 | { name: "mult", type: "float", default: 1 }, 235 | ], 236 | glsl: `return vec4(vec3((0.5-_st.y)*mult),1.0);`, 237 | }); 238 | _hydra.synth.setFunction({ 239 | name: "lengthCenter", 240 | type: "src", 241 | inputs: [ 242 | { name: "mult", type: "float", default: 1 }, 243 | ], 244 | glsl: `return vec4(vec3(length((vec2(0.5)-_st)*mult)),1.0);`, 245 | }); 246 | _hydra.synth.setFunction({ 247 | name: "distanceCenter", 248 | type: "src", 249 | inputs: [ 250 | { name: "px", type: "float", default: 0 }, 251 | { name: "py", type: "float", default: 0 }, 252 | ], 253 | glsl: `return vec4(vec3(length(vec2(0.5)-_st,vec2(0.5)-vec2(px,py))),1.0);`, 254 | }); 255 | 256 | } 257 | -------------------------------------------------------------------------------- /doc/hydra-colorspaces.md: -------------------------------------------------------------------------------- 1 | # hydra-colorspaces 2 | 3 | The hydra-colorspaces extension adds lots of functions to work with color in many different colorspaces. 4 | 5 | ## Available colorspaces 6 | 7 | | Colorspace | Elements | Stands for | Range | Description | 8 | |-----------------|----------|------------|--------|---------------| 9 | | rgb | r,g,b,a | Red, Green, Blue, Alpha | [0;1] [0;1] [0;1] [0;1] | Good old RGBA 10 | | cmyk | c,m,y,k | Cyan, Magenta, Yellow, Key | [0;1] [0;1] [0;1] [0;1] | Substractive colorspace used in print 11 | | hsv | h,s,v | Hue, Saturation, Value (Brightness) | [0;1] [0;1] [0;1] | Cylindrical colorspace where saturation peaks when brightness is 1 12 | | hsl | h,s,l | Hue, Saturation, Lightness | [0;1] [0;1] [0;1] | Cylindrical colorspace where saturation peaks when lightness is 0.5 13 | | yuv | y,u,v | Luminance, Blue difference, Red difference | [0;1] [-0.5;+0.5] [-0.5;+0.5] | Color model used in PAL systems 14 | | yiq | y,i,q | Luminance, In-phase, Quadrature | [0;1] [-0.5;+0.5] [-0.5;+0.5] | Color model used in NTSC systems 15 | 16 | ## List of functions 17 | 18 | Here are all the functions written for quick reference, exemplified using the `hsv` colorspace and the `h` element, however these apply to any colorspace and any element within it: 19 | 20 | ```js 21 | // solid 22 | hsv(1,0.5,1,1).out() 23 | // color 24 | gradient().hsv(1,0.5,1).out() 25 | gradient().hsv.offset(.5,0,-.2).out() 26 | gradient().hsv.invert(1,0,1).out() 27 | gradient().hsv.to().out() 28 | gradient().hsv.from().out() 29 | // element specific 30 | gradient().hsv.h().out() 31 | gradient().hsv.hMult(2).out() 32 | gradient().hsv.hOffset(.2).out() 33 | gradient().hsv.hInvert().out() 34 | gradient().hsv.hSet(0.3).out() 35 | gradient().hsv.hFrom(osc()).out() 36 | gradient().hsv.hMultFrom(osc(),1.5).out() 37 | gradient().hsv.hOffsetFrom(noise(),0.2).out() 38 | gradient().hsv.hKey(0.2).out() 39 | gradient().hsv.hWith(x=>x.pixelate()).out() 40 | ``` 41 | 42 | ## Syntax 43 | 44 | This extensions automatically defines functions for the different colorspaces, so listing them all wouldn't make sense. Instead of naming each colorspace, for the purpose of this documentation, I'll be generically referring to all color spaces as `cs`, and to **any** of their elements as `el`. For example, instead of `.cmyk.cOffset()`, which offsets the Cyan element in the CMYK colorspace, I'll write `.cs.elOffset()`. 45 | 46 | ### Arguments 47 | 48 | If you read `el` as the argument for a function, it means there's an argument for each element of the colorspace in order. For example, `cs(el,alpha)` would mean `yuv(y,u,v,alpha)` for that colorspace. 49 | 50 | ### About alpha 51 | 52 | Some functions add alpha as a last argument after the elements of the colorspace. This doesn't apply to RGBA, given it already has alpha in it. For example `cs(el=0, alpha=1)` means `rgb(r=0,g=0,b=0,a=1)`. 53 | 54 | --- 55 | 56 | ## Functions 57 | 58 | ### Solid colors 59 | 60 | **`cs( el = 0, alpha = 1 )`** 61 | * Example: `hsv(()=>time,1,1).out()` 62 | * Alias: `cs_solid()` 63 | 64 | These functions will act similarly to Hydra's `solid()` function, letting you write a particular color in the given colorspace and filling the screen with it. 65 | 66 | --- 67 | 68 | ### Multiply elements 69 | 70 | **`.cs( el = 1, alpha = 1 )`** 71 | * Example: `osc(20,.1,2).yuv(1,1,0).out()` 72 | * Alias: `.cs.color()`, `.cs_color()` 73 | 74 | These functions will act similarly to Hydra's `.color()` function, allowing you to multiply the value of each element in the colorspace. 75 | 76 | #### Multiply a specific element 77 | 78 | **`.cs.elMult( amt = 1 )`** 79 | * Example: `gradient().hsl.sMult(.8).out()` 80 | * Alias: `.cs_el_mult()` 81 | 82 | --- 83 | 84 | ### Offset elements 85 | 86 | **`.cs.offset( el = 0, alpha = 0 )`** 87 | * Example: `gradient().hsv.offset(.5,-.2,.5).out()` 88 | * Alias: `.cs_offset()` 89 | 90 | Offset an element by a given amount. Basically adds the number you give it to the corresponding element. 91 | 92 | #### Offset a specific element 93 | 94 | **`.cs.elOffset()`** 95 | * Example: `osc().yiq.qOffset(.2).out()` 96 | * Alias: `.cs_el_offset()` 97 | 98 | --- 99 | 100 | ### Invert elements 101 | 102 | **`.cs.invert( el = 1, alpha = 0 )`** 103 | * Example: `gradient().cmyk.invert(1,0,1,0).out()` 104 | * Alias: `.cs_invert()` 105 | 106 | Invert an element by a given amount. The argument sets interpolation between the original color and the inverted one, that's why the default for elements is 1 while alpha is 0. An inversion here means `1.0 - el` for most elements. For those elements with a bipolar range, inversion is done by calculating `0.0 - el` instead. 107 | 108 | #### Invert a specific element 109 | 110 | **`.cs.elInvert()`** 111 | * Example: `osc(5,.1,2).yiq.qInvert().out()` 112 | * Alias: `.cs_el_invert()` 113 | 114 | --- 115 | 116 | ### Converting to a given colorspace 117 | 118 | **`.cs.to()`** 119 | * Example: `gradient().cmyk.to().out()` 120 | * Alias: `.cs_to()` 121 | 122 | Converts the color values of the texture which calls the method to the given colorspace. 123 | 124 | ### Converting from a given colorspace to RGBA 125 | 126 | **`.cs.from()`** 127 | * Example: `gradient().add(solid(0,0,1)).hsv.from().out()` 128 | * Alias: `.cs_from()` 129 | 130 | Let's you convert back to RGBA, or interpret any color values as if they were from the given colorspace. 131 | 132 | --- 133 | 134 | ## Other element specific functions 135 | 136 | ### Retrieving an element 137 | 138 | **`.cs.el()`** 139 | * Example: `osc(40,.1,Math.PI/2).hsv.h().out()` 140 | * Alias: `.cs_el()` 141 | 142 | These functions will work in the same way as Hydra's `r()`, `b()`, `g()`, and `a()` functions. They will simply replicate the given element's value in all channels except alpha and return an opaque image. 143 | 144 | --- 145 | 146 | ### Setting an element 147 | 148 | **`.cs.elSet( value = 1 )`** 149 | * Example: `osc(8,.1,2).hsv.sSet(.5).out()` 150 | * Alias: `.cs_el_set()` 151 | 152 | Allows you to set the given element to a fixed value for all pixels. Shown in the example being used to set the saturation of a texture. 153 | 154 | --- 155 | 156 | ### Modulating an element by a given texture 157 | 158 | **Note:** All the modulating functions take only the first value from the texture, or in other words, the red channel. If you want to modulate by another element in the given texture, simply retrieve that element from the texture as shown just above. 159 | 160 | #### Setting by a given texture 161 | 162 | **`.cs.elFrom( texture , amt = 1 )`** 163 | * Example: `rgb(1,0,0).hsl.sFrom(osc(9)).out()` 164 | * Alias: `.cs_el_from()` 165 | 166 | Will set the value of the element to the value in the given texture. The `amt` argument allows you to interpolate between the original value for the element and the texture's value. 167 | 168 | #### Multiplying by a given texture 169 | 170 | **`.cs.elMultFrom( texture, amt = 1 )`** 171 | * Example: `gradient().hsl.sMultFrom(osc(9)).out()` 172 | * Alias: `.cs_el_mult_from()` 173 | 174 | Will multiply the given element's value by the given texture. The texture's value will be multiplied by the `amt` parameter. 175 | 176 | #### Offsetting by a given texture 177 | 178 | **`.cs.elOffsetFrom( texture, amt = 1 )`** 179 | * Example: `gradient().hsl.hOffsetFrom(noise(1,2),.2).out()` 180 | * Alias: `.cs_el_mult_from()` 181 | 182 | Will add the texture to the given value. The texture's value will be multiplied by the `amt` parameter. 183 | 184 | --- 185 | 186 | ### Keying by a given element 187 | 188 | **`.cs.elKey( topThreshold = 0.5 , topTolerance = 0.05 , bottomThreshold = 0 , bottomTolerance = 0 )`** 189 | * Example: `solid().layer(gradient(1).rgb.rKey(.5,0)).out()` 190 | * Alias: `.cs_el_key()` 191 | 192 | Similar to Hydra's `luma()`, but instead of keying by luminance, you can key by any element. THese functions will turn transparent any pixel where the given element isn't between the top threshold and the bottom threshold. 193 | 194 | **Note:** These functions work with straight alpha, so you might not see changes directly on screen unless you layer it over something else. Similarly to how `osc().color(1,1,1,0).out()` still appears on screen even though it should be completely transparent. If you are someone familiar with straight and premultiplied alpha, you might be interested to join the discussion in [this issue](https://github.com/hydra-synth/hydra-synth/issues/109) I've opened on the hydra-synth repo. 195 | 196 | --- 197 | 198 | ### Applying a transform to a given element 199 | 200 | **`.cs.elWith( f )`** 201 | * Example: `osc(10,.1,2).rotate().hsv.hWith(x=>x.pixelate(16,32)).out()` 202 | * Alias: `.cs_el_with()` 203 | 204 | Inspired by TidalCycles xWith functions, these functions allow you to apply a transform only to a given element. The `f` argument should be a function which receives a texture and returns a texture. 205 | 206 | These are basically syntactic sugar for `texture.cs.elFrom(cloneOfTexture.transform().el())`. This means that in order for these to work Hydra has to render the given texture twice, since its cloning it. So be very cautious about performance when using these. 207 | -------------------------------------------------------------------------------- /hydra-abbreviations.js: -------------------------------------------------------------------------------- 1 | // hydra-abbreviations 2 | // diminutives for all the functions 3 | 4 | { 5 | const getHydra = function () { 6 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 7 | ? "editor" 8 | : window.atom?.packages 9 | ? "atom" 10 | : "idk"; 11 | if (whereami === "editor") { 12 | return window.hydraSynth; 13 | } 14 | if (whereami === "atom") { 15 | return global.atom.packages.loadedPackages["atom-hydra"] 16 | .mainModule.main.hydra; 17 | } 18 | let _h = [ 19 | window.hydraSynth, 20 | window._hydra, 21 | window.hydra, 22 | window.h, 23 | window.H, 24 | window.hy 25 | ].find(h => h?.regl); 26 | return _h; 27 | }; 28 | window._hydra = getHydra(); 29 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 30 | } 31 | 32 | { 33 | const gS = _hydraScope.osc().constructor.prototype; 34 | 35 | gS.bl = gS.blend; 36 | gS.bright = gS.brightness; 37 | gS.br = gS.brightness; 38 | gS.clr = gS.color; 39 | gS.c = gS.color; 40 | gS.clrm = gS.colorama; 41 | gS.cm = gS.colorama; 42 | gS.cntrst = gS.contrast; 43 | gS.ct = gS.contrast; 44 | gS.df = gS.diff; 45 | gS.invrt = gS.invert; 46 | gS.inv = gS.invert; 47 | gS.i = gS.invert; 48 | gS.kld = gS.kaleid; 49 | gS.k = gS.kaleid; 50 | gS.lyr = gS.layer; 51 | gS.ly = gS.layer; 52 | gS.lr = gS.layer; 53 | gS.l = gS.layer; 54 | gS.lm = gS.luma; 55 | gS.msk = gS.mask; 56 | gS.mod = gS.modulate; 57 | gS.m = gS.modulate; 58 | gS.modh = gS.modulateHue; 59 | gS.mh = gS.modulateHue; 60 | gS.mH = gS.modulateHue; 61 | gS.modk = gS.modulateKaleid; 62 | gS.mk = gS.modulateKaleid; 63 | gS.mK = gS.modulateKaleid; 64 | gS.modp = gS.modulatePixelate; 65 | gS.mp = gS.modulatePixelate; 66 | gS.mP = gS.modulatePixelate; 67 | gS.modrp = gS.modulateRepeat; 68 | gS.mrp = gS.modulateRepeat; 69 | gS.mRp = gS.modulateRepeat; 70 | gS.modrpx = gS.modulateRepeatX; 71 | gS.mrpx = gS.modulateRepeatX; 72 | gS.mRpx = gS.modulateRepeatX; 73 | gS.mRpX = gS.modulateRepeatX; 74 | gS.modrpy = gS.modulateRepeatY; 75 | gS.mrpy = gS.modulateRepeatY; 76 | gS.mRpy = gS.modulateRepeatY; 77 | gS.mRpY = gS.modulateRepeatY; 78 | gS.modr = gS.modulateRotate; 79 | gS.mr = gS.modulateRotate; 80 | gS.mR = gS.modulateRotate; 81 | gS.modrt = gS.modulateRotate; 82 | gS.mrt = gS.modulateRotate; 83 | gS.mRt = gS.modulateRotate; 84 | gS.modrot = gS.modulateRotate; 85 | gS.mrot = gS.modulateRotate; 86 | gS.mRot = gS.modulateRotate; 87 | gS.mods = gS.modulateScale; 88 | gS.ms = gS.modulateScale; 89 | gS.mS = gS.modulateScale; 90 | gS.modx = gS.modulateScrollX; 91 | gS.mx = gS.modulateScrollX; 92 | gS.mX = gS.modulateScrollX; 93 | gS.mody = gS.modulateScrollY; 94 | gS.my = gS.modulateScrollY; 95 | gS.mY = gS.modulateScrollY; 96 | gS.mlt = gS.mult; 97 | gS.ml = gS.mult; 98 | gS.mt = gS.mult; 99 | gS.pxlt = gS.pixelate; 100 | gS.px = gS.pixelate; 101 | gS.p = gS.pixelate; 102 | gS.pstr = gS.posterize; 103 | gS.ps = gS.posterize; 104 | gS.rpt = gS.repeat; 105 | gS.rp = gS.repeat; 106 | gS.rptx = gS.repeatX; 107 | gS.rpx = gS.repeatX; 108 | gS.rptX = gS.repeatX; 109 | gS.rpX = gS.repeatX; 110 | gS.rpty = gS.repeatY; 111 | gS.rpy = gS.repeatY; 112 | gS.rptY = gS.repeatY; 113 | gS.rpY = gS.repeatY; 114 | gS.rot = gS.rotate; 115 | gS.rt = gS.rotate; 116 | gS.strt = gS.saturate; 117 | gS.st = gS.saturate; 118 | gS.s = gS.scale; 119 | gS.sc = gS.scale; 120 | gS.scrll = gS.scroll; 121 | gS.xy = gS.scroll; 122 | gS.x = gS.scrollX; 123 | gS.y = gS.scrollY; 124 | gS.shft = gS.shift; 125 | gS.sh = gS.shift; 126 | gS.sb = gS.sub; 127 | gS.sm = gS.sum; 128 | gS.thrsh = gS.thresh; 129 | gS.thr = gS.thresh; 130 | gS.th = gS.thresh; 131 | gS.t = gS.thresh; 132 | 133 | gS.__proto__.o = gS.__proto__.out; 134 | 135 | _hydraScope.n = noise; 136 | _hydraScope.ns = noise; 137 | _hydraScope.vor = voronoi; 138 | _hydraScope.vr = voronoi; 139 | _hydraScope.v = voronoi; 140 | _hydraScope.shp = shape; 141 | _hydraScope.sh = shape; 142 | _hydraScope.grd = gradient; 143 | _hydraScope.gr = gradient; 144 | _hydraScope.gd = gradient; 145 | _hydraScope.s = solid; 146 | _hydraScope.sl = solid; 147 | _hydraScope.o = osc; 148 | 149 | // abbreviator 150 | // TODO: refactor this mess: 151 | 152 | window.Abbreviator = { 153 | abbreviations: { 154 | functions: { 155 | blend: "bl", 156 | bright: "br", 157 | color: "c", 158 | colorama: "cm", 159 | contrast: "ct", 160 | diff: "df", 161 | invert: "inv", 162 | kaleid: "k", 163 | layer: "l", 164 | luma: "lm", 165 | mask: "msk", 166 | modulate: "m", 167 | modulateHue: "mH", 168 | modulateKaleid: "mK", 169 | modulatePixelate: "mP", 170 | modulateRepeat: "mRp", 171 | modulateRepeatX: "mRpX", 172 | modulateRepeatY: "mRpY", 173 | modulateRotate: "mR", 174 | modulateScale: "mS", 175 | modulateScrollX: "mX", 176 | modulateScrollY: "mY", 177 | mult: "mlt", 178 | pixelate: "p", 179 | posterize: "ps", 180 | repeat: "rp", 181 | repeatX: "rpX", 182 | repeatY: "rpY", 183 | rotate: "rt", 184 | saturate: "st", 185 | scale: "sc", 186 | scroll: "xy", 187 | scrollX: "x", 188 | scrollY: "y", 189 | shift: "sh", 190 | sub: "sb", 191 | sum: "sm", 192 | thresh: "th", 193 | }, 194 | sources: { 195 | noise: "ns", 196 | voronoi: "vr", 197 | shape: "sh", 198 | gradient: "grd", 199 | solid: "sl", 200 | }, 201 | }, // here comes some ugly code: 202 | getKeyFromValue(object, value) { 203 | return Object.keys(object).find((key) => object[key] === value); 204 | }, 205 | checkAndUnabbreviateSource(match, offset, string) { 206 | k = match.replace("(", ""); 207 | replace = 208 | Abbreviator.getKeyFromValue( 209 | Abbreviator.abbreviations.sources, 210 | k 211 | ) + "("; 212 | if (offset == 0) return replace; 213 | pch = string[offset - 1]; 214 | if ("\n\t;( ".includes(pch)) return replace; 215 | else return match; 216 | }, 217 | checkAndUnabbreviateFunction(match, offset, string) { 218 | k = match.replace("(", "").replace(".", ""); 219 | replace = 220 | "." + 221 | Abbreviator.getKeyFromValue( 222 | Abbreviator.abbreviations.functions, 223 | k 224 | ) + 225 | "("; 226 | if (offset == 0) return match; 227 | pch = string[offset - 1]; 228 | if ("\n\t )".includes(pch)) return replace; 229 | else return match; 230 | }, 231 | checkAndAbbreviateSource(match, offset, string) { 232 | k = match.replace("(", ""); 233 | replace = Abbreviator.abbreviations.sources[k] + "("; 234 | if (offset == 0) return replace; 235 | pch = string[offset - 1]; 236 | if ("\n\t;( ".includes(pch)) return replace; 237 | else return match; 238 | }, 239 | checkAndAbbreviateFunction(match, offset, string) { 240 | k = match.replace("(", "").replace(".", ""); 241 | replace = "." + Abbreviator.abbreviations.functions[k] + "("; 242 | if (offset == 0) return match; 243 | pch = string[offset - 1]; 244 | if ("\n\t )".includes(pch)) return replace; 245 | else return match; 246 | }, 247 | removeSpacing(code) { 248 | return code 249 | .replaceAll(" ", "") 250 | .replaceAll("\n", "") 251 | .replaceAll("\t", ""); 252 | }, 253 | abbreviate(code, noSpacing = false) { 254 | Object.keys(Abbreviator.abbreviations.functions).forEach((k) => { 255 | code = code.replaceAll( 256 | "." + k + "(", 257 | Abbreviator.checkAndAbbreviateFunction 258 | ); 259 | }); 260 | Object.keys(Abbreviator.abbreviations.sources).forEach((k) => { 261 | code = code.replaceAll( 262 | k + "(", 263 | Abbreviator.checkAndAbbreviateSource 264 | ); 265 | }); 266 | code = noSpacing ? Abbreviator.removeSpacing(code) : code; 267 | console.log(code); 268 | return code; 269 | }, 270 | unabbreviate(code) { 271 | Object.keys(Abbreviator.abbreviations.sources).forEach((k) => { 272 | k = Abbreviator.abbreviations.sources[k]; 273 | code = code.replaceAll( 274 | k + "(", 275 | Abbreviator.checkAndUnabbreviateSource 276 | ); 277 | }); 278 | Object.keys(Abbreviator.abbreviations.functions).forEach((k) => { 279 | k = Abbreviator.abbreviations.functions[k]; 280 | code = code.replaceAll( 281 | "." + k + "(", 282 | Abbreviator.checkAndUnabbreviateFunction 283 | ); 284 | }); 285 | console.log(code); 286 | return code; 287 | }, 288 | }; 289 | window.abbreviate = Abbreviator.abbreviate.bind(Abbreviator); 290 | window.unabbreviate = Abbreviator.unabbreviate.bind(Abbreviator); 291 | } 292 | -------------------------------------------------------------------------------- /old/hydra-blending-modes.js: -------------------------------------------------------------------------------- 1 | // blend modes 2 | // https://www.shadertoy.com/view/XdS3RW 3 | // https://www.w3.org/TR/compositing-1 4 | // https://raw.githubusercontent.com/samarthgulati/hydra-blockly/master/image-editing-glsl-functions.js 5 | 6 | console.log("hydra-blending-modes is deprecated, I recomment using hydra-blend instead") 7 | 8 | var blendmodes_glsl_fns = [ 9 | // vec3 darken( vec3 s, vec3 d ) 10 | // { 11 | // return min(s,d); 12 | // } 13 | { 14 | name: "darken", 15 | type: "combine", 16 | inputs: [], 17 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 18 | vec3 _c1a = _c1.rgb * _c1.a; 19 | return vec4(min(_c0a,_c1a), _c0.a);` 20 | }, 21 | 22 | // vec3 multiply( vec3 s, vec3 d ) 23 | // { 24 | // return s*d; 25 | // } 26 | { 27 | name: "multiply", 28 | type: "combine", 29 | inputs: [], 30 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 31 | vec3 _c1a = _c1.rgb * _c1.a; 32 | return vec4(_c0a * _c1a, _c0.a);` 33 | }, 34 | 35 | // vec3 colorBurn( vec3 s, vec3 d ) 36 | // { 37 | // return 1.0 - (1.0 - d) / s; 38 | // } 39 | { 40 | name: "colorBurn", 41 | type: "combine", 42 | inputs: [], 43 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 44 | vec3 _c1a = _c1.rgb * _c1.a; 45 | return vec4(1.0 - (1.0 - _c1a) / _c0a, _c0.a);` 46 | }, 47 | 48 | // vec3 linearBurn( vec3 s, vec3 d ) 49 | // { 50 | // return s + d - 1.0; 51 | // } 52 | { 53 | name: "linearBurn", 54 | type: "combine", 55 | inputs: [], 56 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 57 | vec3 _c1a = _c1.rgb * _c1.a; 58 | return vec4(_c0a + _c1a - 1.0, _c0.a);` 59 | }, 60 | 61 | // vec3 darkerColor( vec3 s, vec3 d ) 62 | // { 63 | // return (s.x + s.y + s.z < d.x + d.y + d.z) ? s : d; 64 | // } 65 | { 66 | name: "darkerColor", 67 | type: "combine", 68 | inputs: [], 69 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 70 | vec3 _c1a = _c1.rgb * _c1.a; 71 | return (_c0a.r + _c0a.g + _c0a.b < _c1a.r + _c1a.g + _c1a.b) ? vec4(_c0a, _c0.a) : vec4(_c1a, _c0.a);` 72 | }, 73 | 74 | // vec3 lighten( vec3 s, vec3 d ) 75 | // { 76 | // return max(s,d); 77 | // } 78 | { 79 | name: "lighten", 80 | type: "combine", 81 | inputs: [], 82 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 83 | vec3 _c1a = _c1.rgb * _c1.a; 84 | return vec4(max(_c0a,_c1a), _c0.a);` 85 | }, 86 | 87 | // vec3 screen( vec3 s, vec3 d ) 88 | // { 89 | // return s + d - s * d; 90 | // } 91 | { 92 | name: "screen", 93 | type: "combine", 94 | inputs: [], 95 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 96 | vec3 _c1a = _c1.rgb * _c1.a; 97 | return vec4(_c0a + _c1a - _c0a * _c1a, _c0.a);` 98 | }, 99 | 100 | // vec3 colorDodge( vec3 s, vec3 d ) 101 | // { 102 | // return d / (1.0 - s); 103 | // } 104 | { 105 | name: "colorDodge", 106 | type: "combine", 107 | inputs: [], 108 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 109 | vec3 _c1a = _c1.rgb * _c1.a; 110 | return _c0a == vec3(0.) ? vec4(0.) : _c1a == vec3(1.) ? vec4(1.) : vec4(_c1a / (1.0 - _c0a), _c0.a);` 111 | }, 112 | 113 | // vec3 linearDodge( vec3 s, vec3 d ) 114 | // { 115 | // return s + d; 116 | // } 117 | { 118 | name: "linearDodge", 119 | type: "combine", 120 | inputs: [], 121 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 122 | vec3 _c1a = _c1.rgb * _c1.a; 123 | return vec4(_c0a + _c1a, _c0.a);` 124 | }, 125 | 126 | // vec3 lighterColor( vec3 s, vec3 d ) 127 | // { 128 | // return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; 129 | // } 130 | { 131 | name: "lighterColor", 132 | type: "combine", 133 | inputs: [], 134 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 135 | vec3 _c1a = _c1.rgb * _c1.a; 136 | return (_c0a.r + _c0a.g + _c0a.b > _c1a.r + _c1a.g + _c1a.b) ? vec4(_c0a, _c0.a) : vec4(_c1a, _c0.a);` 137 | }, 138 | 139 | // float overlay( float s, float d ) 140 | // { 141 | // return (d < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); 142 | // } 143 | // vec3 overlay( vec3 s, vec3 d ) 144 | // { 145 | // vec3 c; 146 | // c.x = overlay(s.x,d.x); 147 | // c.y = overlay(s.y,d.y); 148 | // c.z = overlay(s.z,d.z); 149 | // return c; 150 | // } 151 | { 152 | name: "overlay", 153 | type: "combine", 154 | inputs: [], 155 | glsl: ` vec4 o; 156 | vec3 _c0a = _c0.rgb * _c0.a; 157 | vec3 _c1a = _c1.rgb * _c1.a; 158 | o.r = (_c1a.r < 0.5) ? 2.0 * _c0a.r * _c1a.r : 1.0 - 2.0 * (1.0 - _c0a.r) * (1.0 - _c1a.r); 159 | o.g = (_c1a.g < 0.5) ? 2.0 * _c0a.g * _c1a.g : 1.0 - 2.0 * (1.0 - _c0a.g) * (1.0 - _c1a.g); 160 | o.b = (_c1a.b < 0.5) ? 2.0 * _c0a.b * _c1a.b : 1.0 - 2.0 * (1.0 - _c0a.b) * (1.0 - _c1a.b); 161 | o.a = _c0.a; 162 | return o;` 163 | }, 164 | 165 | // float softLight( float s, float d ) 166 | // { 167 | // return (s < 0.5) ? d - (1.0 - 2.0 * s) * d * (1.0 - d) 168 | // : (d < 0.25) ? d + (2.0 * s - 1.0) * d * ((16.0 * d - 12.0) * d + 3.0) 169 | // : d + (2.0 * s - 1.0) * (sqrt(d) - d); 170 | // } 171 | // vec3 softLight( vec3 s, vec3 d ) 172 | // { 173 | // vec3 c; 174 | // c.x = softLight(s.x,d.x); 175 | // c.y = softLight(s.y,d.y); 176 | // c.z = softLight(s.z,d.z); 177 | // return c; 178 | // } 179 | { 180 | name: "softLight", 181 | type: "combine", 182 | inputs: [], 183 | glsl: ` vec4 c; 184 | vec3 _c0a = _c0.rgb * _c0.a; 185 | vec3 _c1a = _c1.rgb * _c1.a; 186 | c.r = (_c0a.r < 0.5) ? _c1a.r - (1.0 - 2.0 * _c0a.r) * _c1a.r * (1.0 - _c1a.r) : (_c1a.r < 0.25) ? _c1a.r * ((16.0 * _c1a.r - 12.0) * _c1a.r + 4.0) : _c1a.r + (2.0 * _c0a.r - 1.0) * (sqrt(_c1a.r) - _c1a.r); 187 | c.g = (_c0a.g < 0.5) ? _c1a.g - (1.0 - 2.0 * _c0a.g) * _c1a.g * (1.0 - _c1a.g) : (_c1a.g < 0.25) ? _c1a.g * ((16.0 * _c1a.g - 12.0) * _c1a.g + 4.0) : _c1a.g + (2.0 * _c0a.g - 1.0) * (sqrt(_c1a.g) - _c1a.g); 188 | c.b = (_c0a.b < 0.5) ? _c1a.b - (1.0 - 2.0 * _c0a.b) * _c1a.b * (1.0 - _c1a.b) : (_c1a.b < 0.25) ? _c1a.b * ((16.0 * _c1a.b - 12.0) * _c1a.b + 4.0) : _c1a.b + (2.0 * _c0a.b - 1.0) * (sqrt(_c1a.b) - _c1a.b); 189 | c.a = _c0.a; 190 | //(_c1a.a < 0.5) ? _c0a.a - (1.0 - 2.0 * _c1a.a) * _c0a.a * (1.0 - _c0a.a) : (_c0a.a < 0.25) ? _c0a.a * ((16.0 * _c0a.a - 12.0) * _c0a.a + 4.0) : _c0a.a + (2.0 * _c1a.a - 1.0) * (sqrt(_c0a.a) - _c0a.a); 191 | return c;` 192 | }, 193 | 194 | // float hardLight( float s, float d ) 195 | // { 196 | // return (s < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); 197 | // } 198 | 199 | // vec3 hardLight( vec3 s, vec3 d ) 200 | // { 201 | // vec3 c; 202 | // c.x = hardLight(s.x,d.x); 203 | // c.y = hardLight(s.y,d.y); 204 | // c.z = hardLight(s.z,d.z); 205 | // return c; 206 | // } 207 | 208 | { 209 | name: "hardLight", 210 | type: "combine", 211 | inputs: [], 212 | glsl: ` vec4 c; 213 | vec3 _c0a = _c0.rgb * _c0.a; 214 | vec3 _c1a = _c1.rgb * _c1.a; 215 | c.r = (_c0a.r < 0.5) ? 2.0 * _c0a.r * _c1a.r : 1.0 - 2.0 * (1.0 - _c0a.r) * (1.0 - _c1a.r); 216 | c.g = (_c0a.g < 0.5) ? 2.0 * _c0a.g * _c1a.g : 1.0 - 2.0 * (1.0 - _c0a.g) * (1.0 - _c1a.g); 217 | c.b = (_c0a.b < 0.5) ? 2.0 * _c0a.b * _c1a.b : 1.0 - 2.0 * (1.0 - _c0a.b) * (1.0 - _c1a.b); 218 | c.a = _c0.a; 219 | return c;` 220 | }, 221 | 222 | // float vividLight( float s, float d ) 223 | // { 224 | // return (s < 0.5) ? 1.0 - (1.0 - d) / (2.0 * s) : d / (2.0 * (1.0 - s)); 225 | // } 226 | 227 | // vec3 vividLight( vec3 s, vec3 d ) 228 | // { 229 | // vec3 c; 230 | // c.x = vividLight(s.x,d.x); 231 | // c.y = vividLight(s.y,d.y); 232 | // c.z = vividLight(s.z,d.z); 233 | // return c; 234 | // } 235 | 236 | { 237 | name: "vividLight", 238 | type: "combine", 239 | inputs: [], 240 | glsl: ` vec4 c; 241 | vec3 _c0a = _c0.rgb * _c0.a; 242 | vec3 _c1a = _c1.rgb * _c1.a; 243 | c.r = (_c0a.r < 0.5) ? 1.0 - (1.0 - _c1a.r) / (2.0 * _c0a.r) : _c1a.r / (2.0 * (1.0 - _c0a.r)); 244 | c.g = (_c0a.g < 0.5) ? 1.0 - (1.0 - _c1a.g) / (2.0 * _c0a.g) : _c1a.g / (2.0 * (1.0 - _c0a.g)); 245 | c.b = (_c0a.b < 0.5) ? 1.0 - (1.0 - _c1a.b) / (2.0 * _c0a.b) : _c1a.b / (2.0 * (1.0 - _c0a.b)); 246 | c.a = _c0.a; 247 | return c;` 248 | }, 249 | 250 | // vec3 linearLight( vec3 s, vec3 d ) 251 | // { 252 | // return 2.0 * s + d - 1.0; 253 | // } 254 | 255 | { 256 | name: "linearLight", 257 | type: "combine", 258 | inputs: [], 259 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 260 | vec3 _c1a = _c1.rgb * _c1.a; 261 | return vec4(2.0 * _c0a + _c1a - 1.0, _c0.a);` 262 | }, 263 | 264 | // float pinLight( float s, float d ) 265 | // { 266 | // return (2.0 * s - 1.0 > d) ? 2.0 * s - 1.0 : (s < 0.5 * d) ? 2.0 * s : d; 267 | // } 268 | 269 | // vec3 pinLight( vec3 s, vec3 d ) 270 | // { 271 | // vec3 c; 272 | // c.x = pinLight(s.x,d.x); 273 | // c.y = pinLight(s.y,d.y); 274 | // c.z = pinLight(s.z,d.z); 275 | // return c; 276 | // } 277 | 278 | { 279 | name: "pinLight", 280 | type: "combine", 281 | inputs: [], 282 | glsl: ` vec4 c; 283 | vec3 _c0a = _c0.rgb * _c0.a; 284 | vec3 _c1a = _c1.rgb * _c1.a; 285 | c.r = (2.0 * _c0a.r - 1.0 > _c1a.r) ? 2.0 * _c0a.r - 1.0 : (_c0a.r < 0.5 * _c1a.r) ? 2.0 * _c0a.r : _c1a.r; 286 | c.g = (2.0 * _c0a.g - 1.0 > _c1a.g) ? 2.0 * _c0a.g - 1.0 : (_c0a.g < 0.5 * _c1a.g) ? 2.0 * _c0a.g : _c1a.g; 287 | c.b = (2.0 * _c0a.b - 1.0 > _c1a.b) ? 2.0 * _c0a.b - 1.0 : (_c0a.b < 0.5 * _c1a.b) ? 2.0 * _c0a.b : _c1a.b; 288 | c.a = _c0.a; 289 | return c;` 290 | }, 291 | 292 | // vec3 hardMix( vec3 s, vec3 d ) 293 | // { 294 | // return floor(s + d); 295 | // } 296 | 297 | { 298 | name: "hardMix", 299 | type: "combine", 300 | inputs: [], 301 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 302 | vec3 _c1a = _c1.rgb * _c1.a; 303 | return vec4(floor(_c0a + _c1a), _c0.a);` 304 | }, 305 | 306 | // vec3 difference( vec3 s, vec3 d ) 307 | // { 308 | // return abs(d - s); 309 | // } 310 | 311 | { 312 | name: "difference", 313 | type: "combine", 314 | inputs: [], 315 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 316 | vec3 _c1a = _c1.rgb * _c1.a; 317 | return vec4(abs(_c1a - _c0a), _c0.a);` 318 | }, 319 | 320 | // vec3 exclusion( vec3 s, vec3 d ) 321 | // { 322 | // return s + d - 2.0 * s * d; 323 | // } 324 | { 325 | name: "exclusion", 326 | type: "combine", 327 | inputs: [], 328 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 329 | vec3 _c1a = _c1.rgb * _c1.a; 330 | return vec4(_c0a + _c1a - 2.0 * _c0a * _c1a, _c0.a);` 331 | }, 332 | 333 | // vec3 subtract( vec3 s, vec3 d ) 334 | // { 335 | // return s - d; 336 | // } 337 | { 338 | name: "subtract", 339 | type: "combine", 340 | inputs: [], 341 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 342 | vec3 _c1a = _c1.rgb * _c1.a; 343 | return vec4(_c0a - _c1a, _c0.a);` 344 | }, 345 | 346 | // vec3 divide( vec3 s, vec3 d ) 347 | // { 348 | // return s / d; 349 | // } 350 | { 351 | name: "divide", 352 | type: "combine", 353 | inputs: [], 354 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 355 | vec3 _c1a = _c1.rgb * _c1.a; 356 | return vec4(_c0a / _c1a, _c0.a);` 357 | }, 358 | 359 | // // rgb<-->hsv functions by Sam Hocevar 360 | // // http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl 361 | // vec3 rgb2hsv(vec3 c) 362 | // { 363 | // vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 364 | // vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 365 | // vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 366 | 367 | // float d = q.x - min(q.w, q.y); 368 | // float e = 1.0e-10; 369 | // return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 370 | // } 371 | 372 | // vec3 hsv2rgb(vec3 c) 373 | // { 374 | // vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 375 | // vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 376 | // return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 377 | // } 378 | 379 | // vec3 hue( vec3 s, vec3 d ) 380 | // { 381 | // d = rgb2hsv(d); 382 | // d.x = rgb2hsv(s).x; 383 | // return hsv2rgb(d); 384 | // } 385 | { 386 | name: "hueBlend", 387 | type: "combine", 388 | inputs: [], 389 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 390 | vec3 _c1a = _c1.rgb * _c1.a; 391 | _c1a = _rgbToHsv(_c1a); 392 | _c1a.x = _rgbToHsv(_c0a).x; 393 | return vec4(_hsvToRgb(_c1a), _c0.a);` 394 | }, 395 | 396 | // vec3 color( vec3 s, vec3 d ) 397 | // { 398 | // s = rgb2hsv(s); 399 | // s.z = rgb2hsv(d).z; 400 | // return hsv2rgb(s); 401 | // } 402 | 403 | { 404 | name: "colorBlend", 405 | type: "combine", 406 | inputs: [], 407 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 408 | vec3 _c1a = _c1.rgb * _c1.a; 409 | _c0a = _rgbToHsv(_c0a); 410 | _c0a.z = _rgbToHsv(_c1a).z; 411 | return vec4(_hsvToRgb(_c0a), _c0.a);` 412 | }, 413 | 414 | // vec3 saturation( vec3 s, vec3 d ) 415 | // { 416 | // d = rgb2hsv(d); 417 | // d.y = rgb2hsv(s).y; 418 | // return hsv2rgb(d); 419 | // } 420 | 421 | { 422 | name: "saturationBlend", 423 | type: "combine", 424 | inputs: [], 425 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 426 | vec3 _c1a = _c1.rgb * _c1.a; 427 | _c1a = _rgbToHsv(_c1a); 428 | _c1a.y = _rgbToHsv(_c0a).y; 429 | return vec4(_hsvToRgb(_c1a), _c0.a);` 430 | }, 431 | 432 | // vec3 luminosity( vec3 s, vec3 d ) 433 | // { 434 | // float dLum = dot(d, vec3(0.3, 0.59, 0.11)); 435 | // float sLum = dot(s, vec3(0.3, 0.59, 0.11)); 436 | // float lum = sLum - dLum; 437 | // vec3 c = d + lum; 438 | // float minC = min(min(c.x, c.y), c.z); 439 | // float maxC = max(max(c.x, c.y), c.z); 440 | // if(minC < 0.0) return sLum + ((c - sLum) * sLum) / (sLum - minC); 441 | // else if(maxC > 1.0) return sLum + ((c - sLum) * (1.0 - sLum)) / (maxC - sLum); 442 | // else return c; 443 | // } 444 | { 445 | name: "luminosityBlend", 446 | type: "combine", 447 | inputs: [], 448 | glsl: ` vec3 _c0a = _c0.rgb * _c0.a; 449 | vec3 _c1a = _c1.rgb * _c1.a; 450 | float dLum = dot(_c1a, vec3(0.3, 0.59, 0.11)); 451 | float sLum = dot(_c0a, vec3(0.3, 0.59, 0.11)); 452 | float lum = sLum - dLum; 453 | vec3 c = _c1a + lum; 454 | float minC = min(min(c.x, c.y), c.z); 455 | float maxC = max(max(c.x, c.y), c.z); 456 | if(minC < 0.0) return vec4(sLum + ((c - sLum) * sLum) / (sLum - minC), _c0.a); 457 | else if(maxC > 1.0) return vec4(sLum + ((c - sLum) * (1.0 - sLum)) / (maxC - sLum), _c0.a); 458 | else return vec4(c, _c0.a);` 459 | } 460 | ]; 461 | 462 | blendmodes_glsl_fns.forEach((fn) => {setFunction(fn)}) 463 | blendmodes_list = blendmodes_glsl_fns.map(a => a.name) 464 | 465 | console.log(blendmodes_list) 466 | -------------------------------------------------------------------------------- /hydra-convolutions.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | { 30 | function generateJumps(height, width) { 31 | const middleY = Math.floor(height / 2); 32 | const middleX = Math.floor(width / 2); 33 | 34 | let jumpTable = Array.from({ length: height }, () => []); 35 | 36 | for (let y = 0; y < height; y++) { 37 | const posY = (middleY - y).toFixed(1); 38 | for (let x = 0; x < width; x++) { 39 | const posX = (middleX - x).toFixed(1); 40 | const vec2 = `vec2(${posX}, ${posY})`; 41 | jumpTable[y].push(vec2); 42 | } 43 | } 44 | 45 | return jumpTable.flat(); 46 | } 47 | 48 | function processElement(element){ 49 | return (typeof element === 'string') ? `(${element})` : element.toFixed(9); 50 | } 51 | 52 | function generateWeights(kernel) { 53 | const weights = kernel.flat().map(processElement); 54 | const hasParameters = weights.some(x => x.includes("k")); 55 | weights.hasParameters = hasParameters; 56 | return weights; 57 | } 58 | 59 | function generateConvolutionFunction(obj, settings) { 60 | const name = obj.name + settings.nameSufix; 61 | const kernel = obj.kernel; 62 | const multiplier = processElement(obj.multiplier || 1); 63 | const [height, width] = [kernel.length, kernel[0].length]; 64 | const weights = generateWeights(kernel); 65 | const jumps = generateJumps(height, width); 66 | const hasParameters = weights.hasParameters; 67 | 68 | const { prefix, newLine, sufix } = settings; 69 | 70 | let code = prefix + "\n"; 71 | 72 | weights.forEach((weight, i) => { 73 | const jump = jumps[i]; 74 | if (weight == 0) return; 75 | const line = newLine(weight, jump); 76 | code += line + "\n"; 77 | }); 78 | 79 | code += sufix(multiplier); 80 | 81 | const inputs = [ 82 | { name: '_tex0', type: 'sampler2D', default: o0 }, 83 | { name: 'jump', type: 'float', default: 1 }, 84 | { name: 'amp', type: 'float', default: 1 } 85 | ]; 86 | 87 | if (hasParameters) { 88 | inputs.splice(1, 0, { name: 'k', type: 'float', default: 1 }); 89 | } 90 | 91 | const func = { 92 | name, 93 | type: "src", 94 | inputs, 95 | glsl: code 96 | }; 97 | 98 | return func; 99 | } 100 | 101 | function generateConvolutionFunctionRegular(obj) { 102 | const regularSettings = { 103 | nameSufix: "", 104 | prefix: "vec3 outputColor = vec3(0.0); vec2 res = resolution.xy;", 105 | newLine: (weight, jump) => `outputColor += (${weight}) * texture2D(_tex0, _st + (${jump} * jump / res)).rgb;`, 106 | sufix: (multiplier)=> `return vec4(outputColor * ${multiplier} * amp, texture2D(_tex0, _st).a);` 107 | }; 108 | return generateConvolutionFunction(obj, regularSettings); 109 | } 110 | 111 | function generateConvolutionFunctionForLuma(obj) { 112 | const ySettings = { 113 | nameSufix: "Luma", 114 | prefix: "float outputLuma = 0.0; vec2 res = resolution.xy;", 115 | newLine: (weight, jump) => `outputLuma += (${weight}) * _luminance(texture2D(_tex0, _st + (${jump} * jump / res)).rgb);`, 116 | sufix: (multiplier) => `return vec4(vec3(outputLuma * ${multiplier} * amp), texture2D(_tex0, _st).a);` 117 | }; 118 | return generateConvolutionFunction(obj, ySettings); 119 | } 120 | 121 | function generateConvolutionFunctionForY(obj) { 122 | const ySettings = { 123 | nameSufix: "OnY", 124 | prefix: ` 125 | mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722, -0.09991, -0.33609, 0.43600, 0.615, -0.5586, -0.05639); 126 | mat3 yuv2rgb = mat3(1.0, 0.0, 1.28033, 1.0, -0.21482, -0.38059, 1.0, 2.12798, 0.0); 127 | float outputY = 0.0; 128 | vec2 res = resolution.xy; 129 | `, 130 | newLine: (weight, jump) => `outputY += (${weight}) * (texture2D(_tex0, _st + (${jump} * jump / res)).rgb * rgb2yuv).x;`, 131 | sufix: (multiplier)=> ` 132 | vec4 outputColor = texture2D(_tex0, _st); 133 | vec3 yuv = outputColor.rgb * rgb2yuv; 134 | yuv.x = outputY * ${multiplier}; 135 | outputColor.rgb = yuv * yuv2rgb * amp; 136 | return outputColor; 137 | ` 138 | }; 139 | return generateConvolutionFunction(obj, ySettings); 140 | } 141 | 142 | function generateConvolutionFunctionForUV(obj) { 143 | const uvSettings = { 144 | nameSufix: "OnUV", 145 | prefix: ` 146 | mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722, -0.09991, -0.33609, 0.43600, 0.615, -0.5586, -0.05639); 147 | mat3 yuv2rgb = mat3(1.0, 0.0, 1.28033, 1.0, -0.21482, -0.38059, 1.0, 2.12798, 0.0); 148 | vec2 outputUV = vec2(0.0); 149 | vec2 res = resolution.xy; 150 | `, 151 | newLine: (weight, jump) => `outputUV += (${weight}) * (texture2D(_tex0, _st + (${jump} * jump / res)).rgb * rgb2yuv).yz;`, 152 | sufix: (multiplier) => ` 153 | vec4 outputColor = texture2D(_tex0, _st); 154 | vec3 yuv = outputColor.rgb * rgb2yuv; 155 | yuv.yz = outputUV * ${multiplier}; 156 | outputColor.rgb = yuv * yuv2rgb * amp; 157 | return outputColor; 158 | ` 159 | }; 160 | return generateConvolutionFunction(obj, uvSettings); 161 | } 162 | 163 | function generateConvolutionFunctionForIQ(obj) { 164 | const iqSettings = { 165 | nameSufix: "OnIQ", 166 | prefix: ` 167 | mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.5959, -0.2746, -0.3213, 0.2115, -0.5227, 0.3112); 168 | mat3 yiq2rgb = mat3(1.0, 0.956, 0.619, 1.0, -0.272, -0.647, 1.0, -1.106, 1.703); 169 | vec2 outputIQ = vec2(0.0); 170 | vec2 res = resolution.xy; 171 | `, 172 | newLine: (weight, jump) => `outputIQ += (${weight}) * (texture2D(_tex0, _st + (${jump} * jump / res)).rgb * rgb2yiq).yz;`, 173 | sufix: (multiplier) => ` 174 | vec4 outputColor = texture2D(_tex0, _st); 175 | vec3 yiq = outputColor.rgb * rgb2yiq; 176 | yiq.yz = outputIQ * ${multiplier}; 177 | outputColor.rgb = yiq * yiq2rgb * amp; 178 | return outputColor; 179 | ` 180 | }; 181 | return generateConvolutionFunction(obj, iqSettings); 182 | } 183 | 184 | function setConvolutionFunction(definition) { 185 | const definitions = [ 186 | generateConvolutionFunctionRegular(definition), 187 | generateConvolutionFunctionForLuma(definition), 188 | generateConvolutionFunctionForY(definition), 189 | generateConvolutionFunctionForUV(definition), 190 | generateConvolutionFunctionForIQ(definition) 191 | ] 192 | definitions.forEach(_hydra.synth.setFunction); 193 | } 194 | _hydraScope.setConvolutionFunction = setConvolutionFunction; 195 | 196 | } 197 | 198 | { 199 | // convolution kernel lists 200 | const convolutionKernels = [ 201 | { 202 | name: "sharpen", 203 | kernel: [ 204 | [0, "-k", 0], 205 | ["-k", "(4.0*k)+1.0", "-k"], 206 | [0, "-k", 0] 207 | ] 208 | }, 209 | { 210 | name: "sharpenMore", 211 | kernel: [ 212 | ["-k", "-k", "-k"], 213 | ["-k", "(8.0*k)+1.0", "-k"], 214 | ["-k", "-k", "-k"] 215 | ] 216 | }, 217 | { 218 | name: "lineSharpen", 219 | kernel: [ 220 | ["-k", "(2.0*k)+1.0", "-k"] 221 | ] 222 | }, 223 | { 224 | name: "emboss", 225 | kernel: [ 226 | ["-2.0*k", "-k", 0], 227 | ["-k", 1, "k"], 228 | [0, "k", "2.0*k"] 229 | ] 230 | }, 231 | { 232 | name: "blur", 233 | kernel: [ 234 | [1, 2, 1], 235 | [2, 4, 2], 236 | [1, 2, 1] 237 | ], 238 | multiplier: (1 / 16) 239 | }, 240 | { 241 | name: "blur5", 242 | kernel: [ 243 | [1, 4, 7, 4, 1], 244 | [4, 16, 26, 16, 4], 245 | [7, 26, 41, 26, 7], 246 | [4, 16, 26, 16, 4], 247 | [1, 4, 7, 4, 1] 248 | ], 249 | multiplier: (1 / 273) 250 | }, 251 | { 252 | name: "blur7", 253 | kernel: [ 254 | [0, 0, 1, 2, 1, 0, 0], 255 | [0, 3, 13, 22, 13, 3, 0], 256 | [1, 13, 59, 97, 59, 13, 1], 257 | [2, 22, 97, 159, 97, 22, 2], 258 | [1, 13, 59, 97, 59, 13, 1], 259 | [0, 3, 13, 22, 13, 3, 0], 260 | [0, 0, 1, 2, 1, 0, 0] 261 | ], 262 | multiplier: (1 / 1003) 263 | }, 264 | { 265 | name: "boxBlur", 266 | kernel: [ 267 | [1, 1, 1], 268 | [1, 1, 1], 269 | [1, 1, 1] 270 | ], 271 | multiplier: (1 / 9) 272 | }, 273 | { 274 | name: "boxBlur5", 275 | kernel: [ 276 | [1, 1, 1, 1, 1], 277 | [1, 1, 1, 1, 1], 278 | [1, 1, 1, 1, 1], 279 | [1, 1, 1, 1, 1], 280 | [1, 1, 1, 1, 1] 281 | ], 282 | multiplier: (1 / 25) 283 | }, 284 | { 285 | name: "horizontalBlur", 286 | kernel: [ 287 | [0, 0, 0, 0, 0], 288 | [0, 0, 0, 0, 0], 289 | [1, 1, 1, 1, 1], 290 | [0, 0, 0, 0, 0], 291 | [0, 0, 0, 0, 0] 292 | ], 293 | multiplier: (1 / 5) 294 | }, 295 | { 296 | name: "verticalBlur", 297 | kernel: [ 298 | [0, 0, 1, 0, 0], 299 | [0, 0, 1, 0, 0], 300 | [0, 0, 1, 0, 0], 301 | [0, 0, 1, 0, 0], 302 | [0, 0, 1, 0, 0] 303 | ], 304 | multiplier: (1 / 5) 305 | }, 306 | { 307 | name: "diagonalBlur", 308 | kernel: [ 309 | [0, 0, 0, 0, 1], 310 | [0, 0, 0, 1, 0], 311 | [0, 0, 1, 0, 0], 312 | [0, 1, 0, 0, 0], 313 | [1, 0, 0, 0, 0] 314 | ], 315 | multiplier: (1 / 5) 316 | }, 317 | { 318 | name: "diagonalBlur2", 319 | kernel: [ 320 | [1, 0, 0, 0, 0], 321 | [0, 1, 0, 0, 0], 322 | [0, 0, 1, 0, 0], 323 | [0, 0, 0, 1, 0], 324 | [0, 0, 0, 0, 1] 325 | ], 326 | multiplier: (1 / 5) 327 | }, 328 | { 329 | name: "lineBlur", 330 | kernel: [ 331 | [1, 2, 1] 332 | ], 333 | multiplier: (1 / 4) 334 | }, 335 | { 336 | name: "lineBlur5", 337 | kernel: [ 338 | [7, 26, 41, 26, 7] 339 | ], 340 | multiplier: (1 / 107) 341 | }, 342 | { 343 | name: "sobelY", 344 | kernel: [ 345 | [ 1, 2, 1], 346 | [ 0, 0, 0], 347 | [-1, -2, -1] 348 | ] 349 | }, 350 | { 351 | name: "sobelX", 352 | kernel: [ 353 | [-1, 0, 1], 354 | [-2, 0, 2], 355 | [-1, 0, 1] 356 | ] 357 | }, 358 | { 359 | name: "sobelDiagonal", 360 | kernel: [ 361 | [ 2, 1, 0], 362 | [ 1, 0, -1], 363 | [ 0, -1, -2] 364 | ] 365 | }, 366 | { 367 | name: "sobelDiagonal2", 368 | kernel: [ 369 | [ 0, 1, 2], 370 | [-1, 0, 1], 371 | [-2, -1, 0] 372 | ] 373 | }, 374 | { 375 | name: "prewittY", 376 | kernel: [ 377 | [ 1, 1, 1], 378 | [ 0, 0, 0], 379 | [-1, -1, -1] 380 | ] 381 | }, 382 | { 383 | name: "prewittX", 384 | kernel: [ 385 | [-1, 0, 1], 386 | [-1, 0, 1], 387 | [-1, 0, 1] 388 | ] 389 | }, 390 | { 391 | name: "prewittDiagonal", 392 | kernel: [ 393 | [ 1, 1, 0], 394 | [ 1, 0, -1], 395 | [ 0, -1, -1] 396 | ] 397 | }, 398 | { 399 | name: "prewittDiagonal2", 400 | kernel: [ 401 | [ 0, 1, 1], 402 | [-1, 0, 1], 403 | [-1, -1, 0] 404 | ] 405 | }, 406 | { 407 | name: "edge", 408 | kernel: [ 409 | [-1, -1, -1], 410 | [-1, 8, -1], 411 | [-1, -1, -1] 412 | ] 413 | } 414 | ] 415 | 416 | convolutionKernels.forEach(_hydraScope.setConvolutionFunction) 417 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hyper-hydra 2 | 3 | Extensions for [Hydra](https://github.com/ojack/hydra) focusing on usability. 4 | 5 | ## How to load extensions 6 | 7 | You can load extensions into Hydra with the following syntax: 8 | 9 | ```js 10 | await loadScript("https://hyper-hydra.glitch.me/hydra-src.js") 11 | await loadScript("https://hyper-hydra.glitch.me/hydra-wrap.js") 12 | 13 | osc().out() 14 | ``` 15 | 16 | --- 17 | 18 | ## List of extensions 19 | 20 | Order is merely alphabetical 21 | 22 | ### hydra-abbreviations 23 | 24 | Write very small hydra code. 25 | 26 | [source](./hydra-abbreviations.js) / [url](https://hyper-hydra.glitch.me/hydra-abbreviations.js) 27 | 28 | ```js 29 | o(10, 0.1, 1.2).bl(ns(3)).df(sh(4, 0.6).rt(0, 0.1)).out() 30 | ``` 31 | [open in hydra!](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtYWJicmV2aWF0aW9ucy5qcyUyMiklMEElMEFvKDEwJTJDJTIwMC4xJTJDJTIwMS4yKS5ibChucygzKSkuZGYoc2goNCUyQyUyMDAuNikucnQoMCUyQyUyMDAuMSkpLm91dCgp) 32 | 33 | --- 34 | 35 | ### hydra-arithmetics 36 | 37 | All the functions you needed to make complex visual arithmetics, easily. 38 | 39 | [docs](./doc/hydra-arithmetics.md) / [url](https://hyper-hydra.glitch.me/hydra-arithmetics.js) 40 | 41 | ```js 42 | osc(10,.1,2) 43 | .mod(gradient().asin().cos()) 44 | .step(noise(2).unipolar().div(o0)) 45 | .blend(o0,.2) 46 | .out() 47 | ``` 48 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtYXJpdGhtZXRpY3MuanMlMjIpJTBBJTBBb3NjKDEwJTJDLjElMkMyKSUwQSUwOS5tb2QoZ3JhZGllbnQoKS5hc2luKCkuY29zKCkpJTBBJTA5LnN0ZXAobm9pc2UoMikudW5pcG9sYXIoKS5kaXYobzApKSUwQSUwOS5ibGVuZChvMCUyQy4yKSUwQSUwOS5vdXQoKQ%3D%3D) 49 | 50 | --- 51 | 52 | ### hydra-arrays 53 | 54 | Extends the functionality of arrays in Hydra, letting you operate between different arrays and generate new ones. 55 | 56 | [docs](./doc/hydra-arrays.md) / [url](https://hyper-hydra.glitch.me/hydra-arrays.js) 57 | 58 | ```js 59 | gradient().diff(o0) 60 | .hue([0,2,3,8].div(10).addWrap([0.2,0.1]).smooth()) 61 | .rotate(Array.run(8).mult(Math.PI*2/8)) 62 | .add(shape(64,.02) 63 | .scrollX(Array.random(16,-0.4,0.4).smooth()) 64 | .scrollY(Array.random(16,-0.4,0.4).smooth())) 65 | .blend(o0,.6) 66 | .out() 67 | bpm = 50 68 | ``` 69 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtYXJyYXlzLmpzJTIyKSUwQSUwQWdyYWRpZW50KCklMEElMDkuZGlmZihvMCklMEElMDkuaHVlKCU1QjAlMkMyJTJDMyUyQzglNUQuZGl2KDEwKS5hZGRXcmFwKCU1QjAuMiUyQzAuMSU1RCkuc21vb3RoKCkpJTBBJTA5LnJvdGF0ZShBcnJheS5ydW4oOCkubXVsdChNYXRoLlBJKjIlMkY4KSklMEElMDkuYWRkKCUwQSUyMCUyMCUwOSUwOXNoYXBlKDY0JTJDLjAyKSUwQSUyMCUyMCUwOSUwOSUwOS5zY3JvbGxYKEFycmF5LnJhbmRvbSgxNiUyQy0wLjQlMkMwLjQpLnNtb290aCgpKSUwQSUyMCUyMCUwOSUwOSUwOS5zY3JvbGxZKEFycmF5LnJhbmRvbSgxNiUyQy0wLjQlMkMwLjQpLnNtb290aCgpKSUwQSUwOSklMEElMDkuYmxlbmQobzAlMkMuNiklMEElMDkub3V0KCklMEFicG0lMjAlM0QlMjA1MA%3D%3D) 70 | 71 | --- 72 | 73 | ### hydra-blend 74 | 75 | Adds most blending modes you know from raster image softwares. Ideal for compositing. 76 | 77 | [docs](./doc/hydra-blend.md) / [url](https://hyper-hydra.glitch.me/hydra-blend.js) 78 | 79 | ```js 80 | osc(30) 81 | .screen(noise(3,1).pm()) 82 | .linearBurn(gradient(1).hue(.3)) 83 | .out() 84 | ``` 85 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtYmxlbmQuanMlMjIpJTBBJTBBb3NjKDMwKSUwQSUwOS5zY3JlZW4obm9pc2UoMyUyQzEpLnBtKCkpJTBBJTA5LmxpbmVhckJ1cm4oZ3JhZGllbnQoMSkuaHVlKC4zKSklMEElMDkub3V0KCk%3D) 86 | 87 | --- 88 | 89 | ### hydra-canvas 90 | 91 | Let's you easily control Hydra's canvas. 92 | 93 | [docs](./doc/hydra-canvas.md) / [url](https://hyper-hydra.glitch.me/hydra-canvas.js) 94 | 95 | ```js 96 | setResolution(256,256) 97 | canvas.setRelativeSize(2) 98 | canvas.setAlign("right") 99 | canvas.setLinear() 100 | 101 | solid(1).diff(o0).scale(.5).diff(noise(2,0.4)).out() 102 | ``` 103 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtY2FudmFzLmpzJTIyKSUwQSUwQXNldFJlc29sdXRpb24oMjU2JTJDMjU2KSUwQWNhbnZhcy5zZXRSZWxhdGl2ZVNpemUoMiklMEFjYW52YXMuc2V0QWxpZ24oJTIycmlnaHQlMjIpJTBBY2FudmFzLnNldExpbmVhcigpJTBBJTBBc29saWQoMSkuZGlmZihvMCkuc2NhbGUoLjUpLmRpZmYobm9pc2UoMiUyQzAuNCkpLm91dCgp) 104 | 105 | --- 106 | 107 | ### hydra-colorspaces 108 | 109 | All the function you might need to work with color in different colorspaces such as CMYK, HSV, YUV, etc. 110 | 111 | [docs](./doc/hydra-colorspaces.md) / [url](https://hyper-hydra.glitch.me/hydra-colorspaces.js) 112 | 113 | ```js 114 | gradient().rgb.aSet(0) 115 | .cmyk.from() 116 | .hsv.hOffsetFrom(noise(1,1),.3) 117 | .yuv(1,.5) 118 | .out() 119 | ``` 120 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtY29sb3JzcGFjZXMuanMlMjIpJTBBJTBBZ3JhZGllbnQoKS5yZ2IuYVNldCgwKSUwQSUyMCUyMCUwOS5jbXlrLmZyb20oKSUwQSUwOS5oc3YuaE9mZnNldEZyb20obm9pc2UoMSUyQzEpJTJDLjMpJTBBJTA5Lnl1digxJTJDLjUpJTBBJTA5Lm91dCgp) 121 | 122 | --- 123 | 124 | ### hydra-debug 125 | 126 | **WARNING:** doesn't work in atom / pulsar 127 | 128 | Adds a `.debug()` function that allows you to easily read the fragment shader of your sketch and test changes in real time. 129 | 130 | [docs](./doc/hydra-debug.md) / [url](https://hyper-hydra.glitch.me/hydra-debug.js) 131 | 132 | ```js 133 | osc().rotate().debug(o0) 134 | ``` 135 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtZGVidWcuanMlMjIpJTBBJTBBb3NjKCkucm90YXRlKCkub3V0KCklMjAlMkYlMkYlMjB0cnklMjB0byUyMCU2MGRlYnVnJTYwJTIwbWUlMjAhISUwQSUwQSUyRiUyRiUyMG9zYygpLnJvdGF0ZSgpLmRlYnVnKG8wKQ%3D%3D) 136 | 137 | --- 138 | 139 | ### hydra-fractals 140 | 141 | Adds some functions that when feedbacked are useful for creating fractals. Thanks to [Kali](https://www.shadertoy.com/user/Kali) for the idea. 142 | 143 | [docs](./doc/hydra-fractals.md) / [url](https://hyper-hydra.glitch.me/hydra-fractals.js) 144 | 145 | ```js 146 | src(o0) 147 | .scale(.75) 148 | .add(noise(2,1),.4) 149 | .invert() 150 | .inversion() 151 | .mirrorX2() 152 | .blend(o0,.3) 153 | .out() 154 | ``` 155 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtZnJhY3RhbHMuanMlMjIpJTBBYXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtb3V0cHV0cy5qcyUyMiklMEElMEFvUy5zZXRMaW5lYXIoKSUwQSUwQXNyYyhvMCklMEElMDkuc2NhbGUoLjc1KSUwQSUwOS5hZGQobm9pc2UoMiUyQzEpJTJDLjQpJTBBJTA5LmludmVydCgpJTBBJTA5LmludmVyc2lvbigpJTBBJTA5Lm1pcnJvclgyKCklMEElMDkuYmxlbmQobzAlMkMuMyklMEElMDkub3V0KCk%3D) 156 | 157 | --- 158 | 159 | ### hydra-gif 160 | 161 | **WARNING:** doesn't work on instance mode as of now 162 | 163 | Let's you load `.gif` files into Hydra. 164 | 165 | [docs](./doc/hydra-gif.md) / [url](https://hyper-hydra.glitch.me/hydra-gif.js) 166 | 167 | ```js 168 | s0.initGif('https://i.giphy.com/media/kZqbBT64ECtjy/giphy.gif') 169 | 170 | src(s0) 171 | .out() 172 | ``` 173 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtZ2lmLmpzJTIyKSUwQSUwQXMwLmluaXRHaWYoJ2h0dHBzJTNBJTJGJTJGaS5naXBoeS5jb20lMkZtZWRpYSUyRmtacWJCVDY0RUN0anklMkZnaXBoeS5naWYnKSUwQSUwQXNyYyhzMCkuc2NhbGUoMSUyQy42KSUwQSUwOS5vdXQoKQ%3D%3D) 174 | 175 | --- 176 | 177 | ### hydra-glsl 178 | 179 | Write GLSL code directly in your patches. 180 | 181 | [docs](./doc/hydra-glsl.md) / [url](https://hyper-hydra.glitch.me/hydra-glsl.js) 182 | 183 | ```js 184 | glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) 185 | .glslColor('vec4(c0.brg-(sin(c0.b)*i0),1.)',()=>Math.cos(time)) 186 | .out() 187 | ``` 188 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtZ2xzbC5qcyUyMiklMEElMEFnbHNsKCd2ZWM0KHNpbih1di54KmkwJTJCKHRpbWUqaTEqdmVjMyhpMiUyQ2kyKjIuJTJDaTIqMy4pKSklMkMxLjApJyUyQzE2JTJDMiUyQy4zKSUwQSUwOS5nbHNsQ29sb3IoJ3ZlYzQoYzAuYnJnLShzaW4oYzAuYikqaTApJTJDMS4pJyUyQygpJTNEJTNFTWF0aC5jb3ModGltZSkpJTBBJTA5Lm91dCgp) 189 | 190 | --- 191 | 192 | ### hydra-gradientmap 193 | 194 | Create gradients with css colors and use them for gradient mapping. 195 | 196 | [url](https://hyper-hydra.glitch.me/hydra-gradientmap.js) 197 | 198 | ```js 199 | const myGradient = createGradient("#000", "#0bf", "red", "white"); 200 | 201 | osc(30,.05).kaleid(720).scale(1,()=>innerHeight/innerWidth) 202 | .lookupX(myGradient) 203 | .out() 204 | ``` 205 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlkcmEtZXh0ZW5zaW9ucy5nbGl0Y2gubWUlMkZoeWRyYS1ncmFkaWVudHMuanMlMjIpJTBBJTBBY29uc3QlMjBteUdyYWRpZW50JTIwJTNEJTIwY3JlYXRlR3JhZGllbnQoJTIyJTIzMDAwJTIyJTJDJTIwJTIyJTIzMGJmJTIyJTJDJTIwJTIycmVkJTIyJTJDJTIwJTIyd2hpdGUlMjIpJTNCJTBBJTIwJTBBb3NjKDMwJTJDLjA1KS5rYWxlaWQoNzIwKS5zY2FsZSgxJTJDKCklM0QlM0Vpbm5lckhlaWdodCUyRmlubmVyV2lkdGgpJTBBJTA5Lmxvb2t1cFgobXlHcmFkaWVudCklMEElMDkub3V0KCklMEElMjAlMEFzcmMobXlHcmFkaWVudCkub3V0KG8xKSUwQSUwQXJlbmRlcigp) 206 | 207 | --- 208 | 209 | ### hydra-mouse 210 | 211 | Replaces Hydra's standard `mouse` object adding useful properties. 212 | 213 | [docs](./doc/hydra-mouse.md) / [url](https://hyper-hydra.glitch.me/hydra-mouse.js) 214 | 215 | ```js 216 | noise(1) 217 | .add(shape(64,.01,.2).scrollX(()=>mouse.posx).scrollY(()=>mouse.posy)) 218 | .out() 219 | ``` 220 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtbW91c2UuanMlMjIpJTBBJTBBbm9pc2UoMSklMEElMDkuYWRkKHNoYXBlKDY0JTJDLjAxJTJDLjIpLnNjcm9sbFgoKCklM0QlM0Vtb3VzZS5wb3N4KS5zY3JvbGxZKCgpJTNEJTNFbW91c2UucG9zeSkpJTBBJTA5Lm91dCgp) 221 | 222 | --- 223 | 224 | ### hydra-outputs 225 | 226 | Change the properties of Hydra's outputs' framebuffers. Most importantly: try linear interpolation. 227 | 228 | [docs](./doc/hydra-outputs.md) / [url](https://hyper-hydra.glitch.me/hydra-outputs.js) 229 | 230 | ```js 231 | o1.setLinear() 232 | 233 | src(o1) 234 | .layer(osc(30,.2,1).mask(shape(4,.1,0))) 235 | .scale(1.01).rotate(.01) 236 | .out(o1) 237 | ``` 238 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtb3V0cHV0cy5qcyUyMiklMEElMEFvMC5zZXROZWFyZXN0KCklMEFvMS5zZXRMaW5lYXIoKSUwQSUwQXNyYyhvMCklMEElMjAubGF5ZXIob3NjKDMwJTJDLjIlMkMxKS5tYXNrKHNoYXBlKDQlMkMuMSUyQzApKSklMEElMjAuc2NhbGUoMS4wMSkucm90YXRlKC4wMSklMEElMjAub3V0KG8wKSUwQSUwQXNyYyhvMSklMEElMjAubGF5ZXIob3NjKDMwJTJDLjIlMkMxKS5tYXNrKHNoYXBlKDQlMkMuMSUyQzApKSklMEElMjAuc2NhbGUoMS4wMSkucm90YXRlKC4wMSklMEElMjAub3V0KG8xKSUwQSUwQXJlbmRlcigp) 239 | 240 | --- 241 | 242 | ### hydra-pip 243 | 244 | Adds a function to toggle picture-in-picture. Note that colors might look a bit washed out since this extension uses hydra's canvas' MediaStream. 245 | 246 | [url](https://hyper-hydra.glitch.me/hydra-pip.js) 247 | 248 | ```js 249 | osc().out() 250 | 251 | hydraPictureInPicture() // alias: hydraPip() 252 | ``` 253 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtcGlwLmpzJTIyKSUwQSUwQW9zYyg0MCUyQy4wOSUyQzEuNSklMEElMjAlMjAlMjAuZGlmZihvc2MoMjApLmx1bWEoKSklMEEub3V0KCklMEElMEElMkYlMkYlMjBoeWRyYVBpcCgpJTNC) 254 | 255 | --- 256 | 257 | ### hydra-pixels 258 | 259 | Retrieve pixel values from Hydra's outputs. 260 | 261 | [docs](./doc/hydra-pixels.md) / [url](https://hyper-hydra.glitch.me/hydra-pixels.js) 262 | 263 | ```js 264 | osc(40,.09,1.5) 265 | .diff(osc(20).luma()) 266 | .color(1,1,()=>1+pixel[0]/255) 267 | .out() 268 | 269 | update = ()=> { 270 | pixel = o0.read(width/2,height/2) // center of the screen 271 | } 272 | ``` 273 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtcGl4ZWxzLmpzJTIyKSUwQSUwQW9zYyg0MCUyQy4wOSUyQzEuNSklMEElMjAlMjAlMjAuZGlmZihvc2MoMjApLmx1bWEoKSklMEElMjAlMjAlMjAuY29sb3IoMSUyQzElMkMoKSUzRCUzRTElMkJwaXhlbCU1QjAlNUQlMkYyNTUpJTBBLm91dCgpJTBBJTBBdXBkYXRlJTIwJTNEJTIwKCklM0QlM0UlMjAlN0IlMEElMjAlMjBwaXhlbCUyMCUzRCUyMG8wLnJlYWQod2lkdGglMkYyJTJDaGVpZ2h0JTJGMiklMjAlMkYlMkYlMjBjZW50ZXIlMjBvZiUyMHRoZSUyMHNjcmVlbiUwQSU3RA%3D%3D) 274 | 275 | --- 276 | 277 | ### hydra-src 278 | 279 | Adds `srcAbs` and `srcRel` functions. `srcAbs` will act as `src()` but will show the source with its original width and height on screen. `scrRel` will act as `src()` but will mantain the source's aspect ratio. Works great with [hydra-wrap](#hydra-wrap). There's also `srcMask`, `srcAbsMask` and `srcAbsMark` which will mask out the wrapping. 280 | 281 | [url](https://hyper-hydra.glitch.me/hydra-src.js) 282 | 283 | ```js 284 | src(o0) 285 | .scale(1.01) 286 | .colorama(-.02).brightness(-.2) 287 | .blend(o0,.8) 288 | .layer(srcAbs(s0).luma(.4,.1)) 289 | .out() 290 | ``` 291 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtc3JjLmpzJTIyKSUwQSUwQXMwLmluaXRJbWFnZSgnaHR0cHMlM0ElMkYlMkZ1cGxvYWQud2lraW1lZGlhLm9yZyUyRndpa2lwZWRpYSUyRmNvbW1vbnMlMkYyJTJGMjUlMkZIeWRyYS1Gb3RvLmpwZycpJTBBJTBBc3JjKG8wKSUwQSUwOS5zY2FsZSgxLjAxKSUwQSUyMCUyMCUwOS5jb2xvcmFtYSgtLjAyKS5icmlnaHRuZXNzKC0uMiklMEElMjAlMjAlMDkuYmxlbmQobzAlMkMuOCklMEElMDkubGF5ZXIoc3JjQWJzKHMwKS5sdW1hKC40JTJDLjEpKSUwQSUwOS5vdXQoKQ%3D%3D) 292 | 293 | --- 294 | 295 | ### hydra-swizzle 296 | 297 | Replicates the swizzling functionality from GLSL. 298 | 299 | [docs](./doc/hydra-swizzle.md) / [url](https://hyper-hydra.glitch.me/hydra-swizzle.js) 300 | 301 | ```js 302 | gradient(1).gbg 303 | .layer(osc(30,.1,2).bggr) 304 | .layer(gradient().r.mask(shape(2))) 305 | .out() 306 | ``` 307 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtc3dpenpsZS5qcyUyMiklMEElMEFncmFkaWVudCgxKS5nYmclMEElMDkubGF5ZXIob3NjKDMwJTJDLjElMkMyKS5iZ2dyKSUwQSUwOS5sYXllcihncmFkaWVudCgpLnIubWFzayhzaGFwZSgyKSkpJTBBJTA5Lm91dCgp) 308 | 309 | --- 310 | 311 | ### hydra-tap 312 | 313 | Adds a tap control for bpm and basic envelopes. Inspired by Resolume. 314 | 315 | [docs](./doc/hydra-tap.md) / [url](https://hyper-hydra.glitch.me/hydra-tap.js) 316 | 317 | ```js 318 | osc(30,.01,beats(1)).out() 319 | 320 | osc().rotate(beats_(2).curve(-3)).out() 321 | 322 | osc().scale(beats(1).curve(2).range(1,2)).out() 323 | 324 | // Ctrl + Space Bar for tapping 325 | // Ctrl + , (Comma) for re-sync 326 | ``` 327 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtdGFwLmpzJTIyKSUwQSUwQW9zYygzMCUyQy4wMSUyQ2JlYXRzKDEpKS5vdXQoKSUwQSUwQW9zYygpLnJvdGF0ZShiZWF0c18oMikuY3VydmUoLTMpKS5vdXQoKSUwQSUwQW9zYygpLnNjYWxlKGJlYXRzKDEpLmN1cnZlKDIpLnJhbmdlKDElMkMyKSkub3V0KCklMEElMEElMkYlMkYlMjBDdHJsJTIwJTJCJTIwU3BhY2UlMjBCYXIlMjBmb3IlMjB0YXBwaW5nJTBBJTJGJTJGJTIwQ3RybCUyMCUyQiUyMCUyQyUyMChDb21tYSklMjBmb3IlMjByZS1zeW5j) 328 | 329 | --- 330 | 331 | ### hydra-text 332 | 333 | Adds a text generator to Hydra 334 | 335 | [docs](./doc/hydra-text.md) / [url](https://hyper-hydra.glitch.me/hydra-text.js) 336 | 337 | ```js 338 | hydraText.font = "serif" 339 | hydraText.lineWidth = "2%" 340 | str = " hydra_! " 341 | solid(1,.2) 342 | .blend(src(o0).scale(1.02).colorama(.02)) 343 | .layer(text(str)) 344 | .diff(strokeText(str).modulateScale(noise(1,1), .4)) 345 | .out() 346 | ``` 347 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtdGV4dC5qcyUyMiklMEElMEFoeWRyYVRleHQuZm9udCUyMCUzRCUyMCUyMnNlcmlmJTIyJTBBaHlkcmFUZXh0LmxpbmVXaWR0aCUyMCUzRCUyMCUyMjIlMjUlMjIlMEFzdHIlMjAlM0QlMjAlMjIlMjBoeWRyYV8hJTIwJTIyJTBBc29saWQoMSUyQy4yKSUwQSUwOS5ibGVuZChzcmMobzApLnNjYWxlKDEuMDIpLmNvbG9yYW1hKC4wMikpJTBBJTA5LmxheWVyKHRleHQoc3RyKSklMEElMDkuZGlmZihzdHJva2VUZXh0KHN0cikubW9kdWxhdGVTY2FsZShub2lzZSgxJTJDMSklMkMlMjAuNCkpJTBBJTA5Lm91dCgp) 348 | 349 | --- 350 | 351 | ### hydra-vec4 352 | 353 | Adds wrapper functions that allow you to construct vec4's like you would in GLSL. 354 | 355 | [docs](./doc/hydra-vec4.md) / [url](https://hyper-hydra.glitch.me/hydra-vec4.js) 356 | 357 | ```js 358 | noise() 359 | .mult( vec4( vec3(0.5) , 1 ) ) 360 | .add( vec4( [0.5,0].smooth() ) ) 361 | .layer( 362 | vec4( vec3( [0, 1, 0.5] , vec2( ()=>time%1 ) ) , 1) 363 | .mask(shape(4)) 364 | ) 365 | .out() 366 | ``` 367 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtdmVjNC5qcyUyMiklMEElMEFub2lzZSgpJTBBJTA5Lm11bHQoJTIwdmVjNCglMjB2ZWMzKDAuNSklMjAlMkMlMjAxJTIwKSUyMCklMEElMjAlMjAlMDkuYWRkKCUyMHZlYzQoJTIwJTVCMC41JTJDMCU1RC5zbW9vdGgoKSUyMCklMjApJTBBJTA5LmxheWVyKCUwQSUwOSUwOXZlYzQoJTIwdmVjMyglMjAlNUIwJTJDJTIwMSUyQyUyMDAuNSU1RCUyMCUyQyUyMHZlYzIoJTIwKCklM0QlM0V0aW1lJTI1MSUyMCklMjApJTIwJTJDJTIwMSklMEElMjAlMjAlMDklMDklMDkubWFzayhzaGFwZSg0KSklMEElMDkpJTBBJTIwJTIwJTA5Lm91dCgp) 368 | 369 | --- 370 | 371 | ### hydra-wrap 372 | 373 | Change how Hydra wraps textures, and control the wrapping of generators. 374 | 375 | [docs](./doc/hydra-wrap.md) / [url](https://hyper-hydra.glitch.me/hydra-wrap.js) 376 | 377 | ```js 378 | hydraWrap.setMirror() 379 | 380 | src(o0) 381 | .layer(osc().rotate().mask(shape(4,1,0))) 382 | .scale(.5) 383 | .blend(noise(),.2) 384 | .out() 385 | ``` 386 | [open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlwZXItaHlkcmEuZ2xpdGNoLm1lJTJGaHlkcmEtd3JhcC5qcyUyMiklMEElMEFoeWRyYVdyYXAuc2V0TWlycm9yKCklMEElMEFzcmMobzApJTBBJTA5LmxheWVyKG9zYygpLnJvdGF0ZSgpLm1hc2soc2hhcGUoNCUyQzElMkMwKSkpJTBBJTA5LnNjYWxlKC41KSUwQSUwOS5ibGVuZChub2lzZSgpJTJDLjIpJTBBJTA5Lm91dCgp) 387 | 388 | --- 389 | 390 | ## Also check: 391 | 392 | * [`extra-shaders-for-hydra`](https://gitlab.com/metagrowing/extra-shaders-for-hydra) : another really useful repo of hydra extensions made by Thomas Jourdan 393 | * [`hydra-midi`](https://github.com/arnoson/hydra-midi) : a super complete midi extension for hydra, made by Arnno Schlipf. 394 | * [`hydra-antlia`](https://github.com/geikha/hydra-antlia) : my extension of hydra for colors, geometry and interactivity. 395 | -------------------------------------------------------------------------------- /hydra-colorspaces.js: -------------------------------------------------------------------------------- 1 | { 2 | const getHydra = function () { 3 | const whereami = window.location?.href?.includes("hydra.ojack.xyz") 4 | ? "editor" 5 | : window.atom?.packages 6 | ? "atom" 7 | : "idk"; 8 | if (whereami === "editor") { 9 | return window.hydraSynth; 10 | } 11 | if (whereami === "atom") { 12 | return global.atom.packages.loadedPackages["atom-hydra"] 13 | .mainModule.main.hydra; 14 | } 15 | let _h = [ 16 | window.hydraSynth, 17 | window._hydra, 18 | window.hydra, 19 | window.h, 20 | window.H, 21 | window.hy 22 | ].find(h => h?.regl); 23 | return _h; 24 | }; 25 | window._hydra = getHydra(); 26 | window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; 27 | } 28 | 29 | window.gS = _hydraScope.osc().constructor.prototype; 30 | 31 | { 32 | const hcs = {}; 33 | 34 | hcs.colorspaces = [ 35 | { 36 | name: "rgb", 37 | elems: ["r", "g", "b", "a"], 38 | to: "r = _r; g = _g; b = _b; a = _a;", 39 | from: "_r = r; _g = g; _b = b; _a = a;", 40 | }, 41 | { 42 | name: "cmyk", 43 | elems: ["c", "m", "y", "k"], 44 | to: ` 45 | k = 1.0-max(_r,max(_g,_b)); 46 | c = (1.0-_r-k) / (1.0-k); 47 | m = (1.0-_g-k) / (1.0-k); 48 | y = (1.0-_b-k) / (1.0-k); 49 | `, 50 | from: ` 51 | _r = (1.0-c)*(1.0-k); 52 | _g = (1.0-m)*(1.0-k); 53 | _b = (1.0-y)*(1.0-k); 54 | `, 55 | }, 56 | { 57 | name: "hsv", 58 | elems: ["h", "s", "v"], 59 | to: ` 60 | vec3 _hsv = _rgbToHsv(vec3(_r,_g,_b)); 61 | h = _hsv.x; s = _hsv.y; v = _hsv.z; 62 | `, 63 | from: ` 64 | vec3 _rgb = _hsvToRgb(vec3(h,s,v)); 65 | _r = _rgb.r; _g = _rgb.g; _b = _rgb.b; 66 | `, 67 | }, 68 | { 69 | name: "hsl", 70 | elems: ["h", "s", "l"], 71 | to: ` 72 | vec3 _hsv = _rgbToHsv(vec3(_r,_g,_b)); 73 | h = _hsv.x; 74 | l = _hsv.z*(1.0-(_hsv.y*0.5)); 75 | s = (_hsv.z-l)/(min(l,1.0-l)); 76 | s *= step(-l,-0.000001)*step(l,0.999999); 77 | `, 78 | from: ` 79 | _hsv.x = h; 80 | _hsv.z = l + (s*min(l,1.0-l)); 81 | _hsv.y = 2.0*(1.0-(l/_hsv.z))*step(-_hsv.z,-0.000001); 82 | vec3 _rgb = _hsvToRgb(_hsv); 83 | _r = _rgb.r; _g = _rgb.g; _b = _rgb.b; 84 | `, 85 | }, 86 | { 87 | name: "yuv", 88 | elems: ["y", "u", "v"], 89 | to: ` 90 | mat3 rgb2yuv = mat3(0.2126, 0.7152, 0.0722, -0.09991, -0.33609, 0.43600, 0.615, -0.5586, -0.05639); 91 | vec3 _yuv = vec3(_r,_g,_b) * rgb2yuv; 92 | y = _yuv.x; u = _yuv.y; v = _yuv.z; 93 | `, 94 | from: ` 95 | mat3 yuv2rgb = mat3(1.0, 0.0, 1.28033, 1.0, -0.21482, -0.38059, 1.0, 2.12798, 0.0); 96 | vec3 _rgb = vec3(y,u,v) * yuv2rgb; 97 | _r = _rgb.r; _g = _rgb.g; _b = _rgb.b; 98 | `, 99 | }, 100 | { 101 | name: "yiq", 102 | elems: ["y", "i", "q"], 103 | to: ` 104 | mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.5959, -0.2746, -0.3213, 0.2115, -0.5227, 0.3112); 105 | vec3 _yiq = vec3(_r,_g,_b) * rgb2yiq; 106 | y = _yiq.x; i = _yiq.y; q = _yiq.z; 107 | `, 108 | from: ` 109 | mat3 yiq2rgb = mat3(1.0, 0.956, 0.619, 1.0, -0.272, -0.647, 1.0, -1.106, 1.703); 110 | vec3 _rgb = vec3(y,i,q) * yiq2rgb; 111 | _r = _rgb.r; _g = _rgb.g; _b = _rgb.b; 112 | `, 113 | }, 114 | ]; 115 | 116 | // utils 117 | 118 | hcs.generateInputAssignment = function (elems, format) { 119 | return format 120 | ? elems 121 | .map((el) => { 122 | let assign = format; 123 | if ( 124 | "yuvalpha,yiqalpha".includes(elems.join("")) && 125 | "uviq".includes(el) && 126 | assign.includes("1.0 - #el") 127 | ) { 128 | assign = assign.replaceAll("1.0 - #el", "0.0 - #el"); 129 | } // TODO: find a better solution for this patch on inversion 130 | return assign 131 | .replaceAll("#el", el) 132 | .replaceAll("#in", "in_" + el); 133 | }) 134 | .join("") 135 | : ""; 136 | }; 137 | 138 | hcs.generateDirectAssignment = function (elems, tofrom) { 139 | const rgba = ["_r", "_g", "_b", "_a"]; 140 | return tofrom == "to" 141 | ? elems.map((el, i) => rgba[i] + " = " + el + ";").join("") 142 | : elems.map((el, i) => el + " = " + rgba[i] + ";").join(""); 143 | }; 144 | 145 | hcs.generateDeclarations = function (elems, type = "float") { 146 | return elems.map((el) => type + " " + el + ";\n").join(""); 147 | }; 148 | 149 | // functions that use all elems 150 | 151 | hcs.generateFunction = function ({ 152 | colorspace, 153 | sufix, 154 | type, 155 | assignmentFormat, 156 | inputDefault, 157 | alphaDefault = 1, 158 | tofrom, 159 | }) { 160 | const name = colorspace.name + (sufix ? "_" + sufix : ""); 161 | 162 | const hasColorInput = ["color", "combine"].includes(type); 163 | 164 | const isRgb = "rgba".includes(colorspace.name); 165 | const elems = 166 | isRgb || tofrom 167 | ? colorspace.elems 168 | : colorspace.elems.concat("alpha"); 169 | 170 | const inputs = assignmentFormat 171 | ? elems.map((el) => ({ 172 | type: "float", 173 | name: "in_" + el, 174 | default: inputDefault, 175 | })) 176 | : []; 177 | inputs.length ? (inputs.at(-1).default = alphaDefault) : null; 178 | 179 | const rgbaDeclarations = hcs.generateDeclarations([ 180 | "_r", 181 | "_g", 182 | "_b", 183 | "_a", 184 | ]); 185 | const rgbaAssignments = hasColorInput 186 | ? "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;" 187 | : ""; 188 | 189 | const elemDeclarations = 190 | hcs.generateDeclarations(elems) + 191 | (isRgb || tofrom ? "" : "alpha = _a;"); 192 | const to = hasColorInput || tofrom == "to" ? colorspace.to : ""; 193 | const elemAssignments = assignmentFormat 194 | ? hcs.generateInputAssignment(elems, assignmentFormat) 195 | : hcs.generateDirectAssignment(elems, tofrom); 196 | const from = !tofrom || tofrom == "from" ? colorspace.from : ""; 197 | 198 | const returner = 199 | (isRgb || tofrom ? "" : "_a = alpha;") + 200 | "return vec4(_r,_g,_b,_a);"; 201 | 202 | const glsl = 203 | rgbaDeclarations + 204 | rgbaAssignments + 205 | elemDeclarations + 206 | to + 207 | elemAssignments + 208 | from + 209 | returner; 210 | 211 | return { name: name, type: type, inputs: inputs, glsl: glsl }; 212 | }; 213 | 214 | hcs.generateColorFunction = (cs) => 215 | hcs.generateFunction({ 216 | colorspace: cs, 217 | sufix: "color", 218 | type: "color", 219 | assignmentFormat: "#el *= #in;", 220 | inputDefault: 1, 221 | alphaDefault: 1, 222 | }); 223 | hcs.generateOffsetFunction = (cs) => 224 | hcs.generateFunction({ 225 | colorspace: cs, 226 | sufix: "offset", 227 | type: "color", 228 | assignmentFormat: "#el += #in;", 229 | inputDefault: 0, 230 | alphaDefault: 0, 231 | }); 232 | hcs.generateSolidFunction = (cs) => 233 | hcs.generateFunction({ 234 | colorspace: cs, 235 | sufix: "solid", 236 | type: "src", 237 | assignmentFormat: "#el = #in;", 238 | inputDefault: 0, 239 | alphaDefault: 1, 240 | }); 241 | hcs.generateToFunction = (cs) => 242 | hcs.generateFunction({ 243 | colorspace: cs, 244 | sufix: "to", 245 | type: "color", 246 | assignmentFormat: undefined, 247 | inputDefault: 0, 248 | alphaDefault: 1, 249 | tofrom: "to", 250 | }); 251 | hcs.generateFromFunction = (cs) => 252 | hcs.generateFunction({ 253 | colorspace: cs, 254 | sufix: "from", 255 | type: "color", 256 | assignmentFormat: undefined, 257 | inputDefault: 0, 258 | alphaDefault: 1, 259 | tofrom: "from", 260 | }); 261 | hcs.generateInvertFunction = (cs) => 262 | hcs.generateFunction({ 263 | colorspace: cs, 264 | sufix: "invert", 265 | type: "color", 266 | assignmentFormat: "#el = mix(#el, 1.0 - #el, #in);", 267 | inputDefault: 1, 268 | alphaDefault: 0, 269 | }); 270 | 271 | // elem functions 272 | 273 | hcs.generateElementFunction = function (colorspace, elem) { 274 | const name = colorspace.name + "_" + elem; 275 | 276 | const type = "color"; 277 | 278 | const rgbaDeclarations = hcs.generateDeclarations([ 279 | "_r", 280 | "_g", 281 | "_b", 282 | "_a", 283 | ]); 284 | const rgbaAssignments = 285 | "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;"; 286 | 287 | const elemDeclarations = hcs.generateDeclarations(colorspace.elems); 288 | const to = colorspace.to; 289 | 290 | const returner = "return vec4(vec3(" + elem + "),1.0);"; 291 | 292 | const glsl = 293 | rgbaDeclarations + 294 | rgbaAssignments + 295 | elemDeclarations + 296 | to + 297 | returner; 298 | 299 | return { name: name, type: type, inputs: [], glsl: glsl }; 300 | }; 301 | 302 | hcs.generateElementFunctions = (cs) => 303 | cs.elems.map((elem) => hcs.generateElementFunction(cs, elem)); 304 | 305 | hcs.generateSetElementFunction = function ({ 306 | colorspace, 307 | elem, 308 | sufix, 309 | assignmentFormat = "#el = #in;", 310 | inputDefault = 1, 311 | }) { 312 | const name = colorspace.name + "_" + elem + "_" + sufix; 313 | 314 | const type = "color"; 315 | 316 | const inputs = [ 317 | { type: "float", name: "in_" + elem, default: inputDefault }, 318 | ]; 319 | 320 | const rgbaDeclarations = hcs.generateDeclarations([ 321 | "_r", 322 | "_g", 323 | "_b", 324 | "_a", 325 | ]); 326 | const rgbaAssignments = 327 | "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;"; 328 | 329 | const elemDeclarations = hcs.generateDeclarations(colorspace.elems); 330 | const to = colorspace.to; 331 | const elemAssignment = hcs.generateInputAssignment( 332 | [elem], 333 | assignmentFormat 334 | ); 335 | const from = colorspace.from; 336 | 337 | const returner = "return vec4(_r,_g,_b,_a);"; 338 | 339 | const glsl = 340 | rgbaDeclarations + 341 | rgbaAssignments + 342 | elemDeclarations + 343 | to + 344 | elemAssignment + 345 | from + 346 | returner; 347 | 348 | return { name: name, type: type, inputs: inputs, glsl: glsl }; 349 | }; 350 | 351 | hcs.generateSetElementFunctions = (cs) => 352 | cs.elems.map((elem) => 353 | hcs.generateSetElementFunction({ 354 | colorspace: cs, 355 | elem, 356 | sufix: "set", 357 | assignmentFormat: "#el = #in;", 358 | }) 359 | ); 360 | hcs.generateOffsetElementFunctions = (cs) => 361 | cs.elems.map((elem) => 362 | hcs.generateSetElementFunction({ 363 | colorspace: cs, 364 | elem, 365 | sufix: "offset", 366 | assignmentFormat: "#el += #in;", 367 | inputDefault: 1, 368 | }) 369 | ); 370 | hcs.generateMultElementFunctions = (cs) => 371 | cs.elems.map((elem) => 372 | hcs.generateSetElementFunction({ 373 | colorspace: cs, 374 | elem, 375 | sufix: "mult", 376 | assignmentFormat: "#el *= #in;", 377 | inputDefault: 1, 378 | }) 379 | ); 380 | hcs.generateInvertElementFunctions = (cs) => 381 | cs.elems.map((elem) => 382 | hcs.generateSetElementFunction({ 383 | colorspace: cs, 384 | elem, 385 | sufix: "invert", 386 | assignmentFormat: "#el = mix(#el, 1.0 - #el, #in);", 387 | inputDefault: 1, 388 | }) 389 | ); 390 | 391 | hcs.generateCombineElementFunction = function ({ 392 | colorspace, 393 | elem, 394 | sufix, 395 | assignmentFormat = "#el = _c1.r;", 396 | inputDefault = 1, 397 | }) { 398 | const name = colorspace.name + "_" + elem + "_" + sufix; 399 | 400 | const type = "combine"; 401 | 402 | const inputs = [{ type: "float", name: "_amt", default: inputDefault }]; 403 | 404 | const rgbaDeclarations = hcs.generateDeclarations([ 405 | "_r", 406 | "_g", 407 | "_b", 408 | "_a", 409 | ]); 410 | const rgbaAssignments = 411 | "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;"; 412 | 413 | const elemDeclarations = hcs.generateDeclarations(colorspace.elems); 414 | const to = colorspace.to; 415 | const elemAssignment = hcs.generateInputAssignment( 416 | [elem], 417 | assignmentFormat 418 | ); 419 | const from = colorspace.from; 420 | 421 | const returner = "return vec4(_r,_g,_b,_a);"; 422 | 423 | const glsl = 424 | rgbaDeclarations + 425 | rgbaAssignments + 426 | elemDeclarations + 427 | to + 428 | elemAssignment + 429 | from + 430 | returner; 431 | 432 | return { name: name, type: type, inputs: inputs, glsl: glsl }; 433 | }; 434 | 435 | hcs.generateSetElementFromFunctions = (cs) => 436 | cs.elems.map((elem) => 437 | hcs.generateCombineElementFunction({ 438 | colorspace: cs, 439 | elem, 440 | sufix: "from", 441 | assignmentFormat: "#el = mix(#el,_c1.r,_amt);", 442 | inputDefault: 1, 443 | }) 444 | ); 445 | hcs.generateOffsetElementFromFunctions = (cs) => 446 | cs.elems.map((elem) => 447 | hcs.generateCombineElementFunction({ 448 | colorspace: cs, 449 | elem, 450 | sufix: "offset_from", 451 | assignmentFormat: "#el += _c1.r*_amt;", 452 | inputDefault: 1, 453 | }) 454 | ); 455 | hcs.generateMultElementFromFunctions = (cs) => 456 | cs.elems.map((elem) => 457 | hcs.generateCombineElementFunction({ 458 | colorspace: cs, 459 | elem, 460 | sufix: "mult_from", 461 | assignmentFormat: "#el *= _c1.r*_amt;", 462 | inputDefault: 1, 463 | }) 464 | ); 465 | 466 | // keying 467 | 468 | hcs.generateKeyingElementFunction = function ({ colorspace, elem }) { 469 | const name = colorspace.name + "_" + elem + "_" + "key"; 470 | 471 | const type = "color"; 472 | 473 | const inputs = [ 474 | { type: "float", name: "_th0", default: 0.5 }, 475 | { type: "float", name: "_t0", default: 0.05 }, 476 | { type: "float", name: "_th1", default: 0 }, 477 | { type: "float", name: "_t1", default: 0 }, 478 | ]; 479 | 480 | const isRgb = "rgba".includes(colorspace.name); 481 | const rgbaDeclarations = hcs.generateDeclarations([ 482 | "_r", 483 | "_g", 484 | "_b", 485 | "_a", 486 | ]); 487 | const rgbaAssignments = 488 | "_r = _c0.r; _g = _c0.g; _b = _c0.b; _a = _c0.a;"; 489 | 490 | const elemDeclarations = hcs.generateDeclarations(colorspace.elems); 491 | const to = colorspace.to; 492 | const keying = ( 493 | "float _key = smoothstep(_th0-(_t0+0.0000001), _th0+(_t0+0.0000001), #elem);" + 494 | "_th1 = 1.0 - _th1 + 0.0000001; _key *= smoothstep(_th1-(-_t1-0.0000001), _th1+(-_t1-0.0000001), #elem);" + 495 | (isRgb ? "a" : "_a") + 496 | " *= _key;" 497 | ).replaceAll("#elem", elem); 498 | const from = colorspace.from; 499 | 500 | const returner = "return vec4(_r,_g,_b,_a);"; 501 | 502 | const glsl = 503 | rgbaDeclarations + 504 | rgbaAssignments + 505 | elemDeclarations + 506 | to + 507 | keying + 508 | from + 509 | returner; 510 | 511 | return { name: name, type: type, inputs: inputs, glsl: glsl }; 512 | }; 513 | 514 | hcs.generateKeyingElementFunctions = (cs) => 515 | cs.elems.map((elem) => 516 | hcs.generateKeyingElementFunction({ 517 | colorspace: cs, 518 | elem, 519 | }) 520 | ); 521 | 522 | // updaters 523 | 524 | hcs.updateFunctions = function () { 525 | [] 526 | .concat( 527 | hcs.colorspaces.map(hcs.generateColorFunction), 528 | hcs.colorspaces.map(hcs.generateOffsetFunction), 529 | hcs.colorspaces.map(hcs.generateSolidFunction), 530 | hcs.colorspaces.map(hcs.generateToFunction), 531 | hcs.colorspaces.map(hcs.generateFromFunction), 532 | hcs.colorspaces.map(hcs.generateInvertFunction), 533 | hcs.colorspaces.map(hcs.generateElementFunctions), 534 | hcs.colorspaces.map(hcs.generateSetElementFunctions), 535 | hcs.colorspaces.map(hcs.generateOffsetElementFunctions), 536 | hcs.colorspaces.map(hcs.generateMultElementFunctions), 537 | hcs.colorspaces.map(hcs.generateInvertElementFunctions), 538 | hcs.colorspaces.map(hcs.generateSetElementFromFunctions), 539 | hcs.colorspaces.map(hcs.generateOffsetElementFromFunctions), 540 | hcs.colorspaces.map(hcs.generateMultElementFromFunctions), 541 | hcs.colorspaces.map(hcs.generateKeyingElementFunctions) 542 | ) 543 | .flat(99) 544 | .filter((x) => x) 545 | .forEach((x) => _hydra.synth.setFunction(x)); 546 | }; 547 | 548 | hcs.cloneGlslSource = function (_tex) { 549 | const tex = Object.assign({}, _tex); 550 | Object.setPrototypeOf(tex, gS); 551 | tex.transforms = Array.from(_tex.transforms); 552 | return tex; 553 | }; 554 | 555 | hcs.updateWithFunctions = function () { 556 | hcs.colorspaces.forEach((cs) => { 557 | cs.elems.forEach((el) => { 558 | gS[cs.name + "_" + el + "_" + "with"] = function (f) { 559 | const tex = hcs.cloneGlslSource(this); 560 | return this[cs.name + "_" + el + "_" + "from"]( 561 | f(tex)[cs.name + "_" + el]() 562 | ); 563 | }; 564 | }); 565 | }); 566 | }; 567 | 568 | hcs.update = function () { 569 | hcs.updateFunctions(); 570 | hcs.updateWithFunctions(); 571 | hcs.colorspaces.forEach((cs) => { 572 | let getterDefinition = 573 | "Object.defineProperty(gS, '#cs', { configurable: true, get: function() {" + 574 | "const func = this.#cs_color.bind(this);"; 575 | getterDefinition += 576 | "const props = {" + 577 | "color: this.#cs_color.bind(this)," + 578 | "offset: this.#cs_offset.bind(this)," + 579 | "to: this.#cs_to.bind(this)," + 580 | "from: this.#cs_from.bind(this)," + 581 | "invert: this.#cs_invert.bind(this),"; 582 | cs.elems.forEach((elem) => { 583 | getterDefinition += 584 | "#elem: this.#cs_#elem.bind(this)," + 585 | "#elemSet: this.#cs_#elem_set.bind(this)," + 586 | "#elemOffset: this.#cs_#elem_offset.bind(this)," + 587 | "#elemMult: this.#cs_#elem_mult.bind(this)," + 588 | "#elemInvert: this.#cs_#elem_invert.bind(this)," + 589 | "#elemFrom: this.#cs_#elem_from.bind(this)," + 590 | "#elemOffsetFrom: this.#cs_#elem_offset_from.bind(this)," + 591 | "#elemMultFrom: this.#cs_#elem_mult_from.bind(this)," + 592 | "#elemKey: this.#cs_#elem_key.bind(this)," + 593 | "#elemWith: this.#cs_#elem_with.bind(this),"; 594 | getterDefinition = getterDefinition.replaceAll("#elem", elem); 595 | }); 596 | getterDefinition += "};"; 597 | getterDefinition += 598 | "Object.assign(func,props);" + "return func; }, });"; 599 | getterDefinition += "_hydraScope.#cs = _hydraScope.#cs_solid;"; 600 | getterDefinition = getterDefinition.replaceAll("#cs", cs.name); 601 | window.eval(getterDefinition); 602 | }); 603 | }; 604 | 605 | hcs.update(); 606 | 607 | window.hydraColorspaces = {}; 608 | hydraColorspaces.colorspaces = hcs.colorspaces; 609 | hydraColorspaces.update = hcs.update.bind(hcs); 610 | } 611 | --------------------------------------------------------------------------------