diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..d4c4e7d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: ritchse +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/README.md b/README.md index e98144f..b2d8f0c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ -# Hydra-strudel-extension -Hydra extension to support strudel code and mini-notation +# hydra-strudel +[WIP] + +made by olivia & TACHA~ + +### Usage +```javascript +await loadScript("https://hydra-strudel.glitch.me/hydra-strudel.js") +``` + +### Example +[live](https://hydra.ojack.xyz/dev/?code=JTJGJTJGJTIwbGljZW5zZWQlMjB3aXRoJTIwQ0MlMjBCWS1OQy1TQSUyMDQuMCUyMGh0dHBzJTNBJTJGJTJGY3JlYXRpdmVjb21tb25zLm9yZyUyRmxpY2Vuc2VzJTJGYnktbmMtc2ElMkY0LjAlMkYlMEFhd2FpdCUyMGxvYWRTY3JpcHQoJTIyaHR0cHMlM0ElMkYlMkZoeWRyYS1zdHJ1ZGVsLmdsaXRjaC5tZSUyRmh5ZHJhLXN0cnVkZWwuanMlMjIpJTBBJTBBJTBBJTJGJTJGbXlQYXR0ZXJuJTIwJTNEJTIwbWluaSgnMCUyMDEwJTIwJTVCMiUyMDUxJTVEKjQnKS5zbG93KDIpJTBBJTJGJTJGbm90ZShteVBhdHRlcm4pLnBsYXkoKSUwQSUyRiUyRm9zYygoKSUyMCUzRCUzRSUyMDEwJTIwKiUyMG15UGF0dGVybi52YWx1ZSgpKS5vdXQoKSUwQSUyRiUyRnNodXNoKCklMEElMEElMEElMEE%3D) +```javascript +await loadScript("https://hydra-strudel.glitch.me/hydra-strudel.js") + + +//myPattern = mini('0 10 [2 51]*4').slow(2) +//note(myPattern).play() +//osc(() => 10 * myPattern.value()).out() +//shush() + +``` +### To do: + +- only run after load // fix loading error +- why not synced? +- add key command for stopping sound +- make strings evaluate directly +- check whether already loaded and only load once +- make always live? +- `hush()` in HYDRA not working + +# diff --git a/doc/ES/hydra-glsl.md b/doc/ES/hydra-glsl.md new file mode 100644 index 0000000..b346253 --- /dev/null +++ b/doc/ES/hydra-glsl.md @@ -0,0 +1,83 @@ +# hydra-glsl + +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 + +## Ejemplo: +```js +glsl('vec4(sin(((_st.x*54.)+time*2.)*vec3(0.1,0.102,0.101)),1.0)') + .diff(o0) + .glslColor('vec4(c0.brg,1.)') + .glslCoord('xy*=(1.0/vec2(i0, i0)); return xy',.25) + .glslCombine('return c0-c1',o1) + .glslCombineCoord('uv+(vec2(c0.r,c0.b)*0.1)',o1) + .out() +``` +[Miralo en hydra!](https://hydra.ojack.xyz/?sketch_id=agiUw1vmrGmmf4Zy) + +--- + +## Funciones: + +| función | argumentos dsps del código | tipo correspondiente | argumentos de glsl : aliases | +|--------------------------|------------------|--------------------|-----------------------------------| +| glsl() | ...args | 'src' | _st : st, uv, xy | +| osc().glslColor() | ...args | 'color' | _c0 : c0, color | +| osc().glslCoord() | ...args | 'coord' | _st : st, uv, xy | +| osc().glslCombine() | texture, ...args | 'combine' | _c0 : c0, color0; _c1: c1, color1 | +| osc().glslCombineCoord() | texture, ...args | 'combineCoord' | _st : st, uv, xy; _c0: c0, color | + +*Nótese que `osc()` es meramente un ejemplo* + +### Extra functions + +| función | argumentos dsps del código | tipo correspondiente | descripción | +|--------------------------|------------------|--------------------|-----------------------------------| +| osc().glslHsv() | ...args | 'color' | Convierte los colores de c0 a HSV. Podés acceder y modificar estos valores desde un `vec3 hsv` | + +--- + +## Argumentos + +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]`. + +#### Importante: +Todos los argumentos son del tipo `float`. + +### Ejemplo: + +```js +glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) + .glslColor('vec4(c0.brg-(sin(c0.b)*miVariable),1.)',['miVariable',0.2]) + .out() +``` + +Hasta podés mandar funciones como variables como se suele hacer en Hydra: + +```js +glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) + .glslColor('vec4(c0.brg-(sin(c0.b)*miVariable),1.)',['miVariable',()=>time%1]) + .out() +``` + +--- + +## Sobre el codigo (y el lazy code) + +* Podés usar de una cualquiera de los aliases descritos arriba. +* Podés omitir el semicolón al final. +* Podés omitir la palabra reservada "return". +* Podes mandar código que ocupe múltiples líneas. + * Solo podés omitir el semicolón en la ultima linea. + * Podés aún así omitir la palabra return. +* 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. +* No podes usar el mismo nombre de un alias como el nombre de un argumento + +--- + +## Tip + +Tené la consola del browser abierta. Hydra no tira errores de frag a la consola integrada. + +## Nota + +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. diff --git a/doc/hydra-arithmetics.md b/doc/hydra-arithmetics.md new file mode 100644 index 0000000..5b28ba7 --- /dev/null +++ b/doc/hydra-arithmetics.md @@ -0,0 +1,113 @@ +# hydra-arithmetics + +Adds many functions related to visual arithmetics. + +## Operators + +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. + +### div + +`.div( divisor )` + +Divide a texture by some value or texture + +### add, sub, mult + +| single value | texture | +|----------------|------------------------| +|`.add( value )` | `.add( tex , amount )` | +|`.sub( value )` | `.sub( tex , amount )` | +|`.mult( value )`| `.mult( tex , amount )`| + +These act the same as in regular Hydra, with the added functionality of working with regular values too. + +### mod + +`.mod( x )` + +Calculates the modulo `x` for the texture that calls it. + +### min, max + +`.min( x )`, `.max( x )` + +Returns the minimum or maximum value when compared to the texture that calls it, accordingly. + +### step + +`.step( x )` + +Generates a step function by comparing x to the texture that calls it. + +### amp, offset + +`.amp( singleValue ) == .amplitude( singleValue )`, `.offset( singleValue ) == .off( singleValue )` + +These are basically aliases for `.mult()` and `.add()` for single values. + +--- + +## Bipolar and unipolar + +### bipolar + +`.bipolar( amp = 1 )` + +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. + +### unipolar + +`.unipolar( amp = 1 )` + +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. + +--- + +## Ranges + +### range + +`.range( min = 0, max = 1 )` + +Allows you to change the range the range of unipolar values to one between `min` and `max`. + +### birange + +`.birange( min = 0, max = 1 )` + +Allows you to change the range the range of bipolar values to one between `min` and `max`. + +### clamp + +`.clamp( min = 0, max = 1 )` + +`clamp` will constrain values between a range from `min` to `max`. + +--- + +## Other transformations + +This extension also adds many argument-less transforms. You can use them as such, for example: `osc().abs()`. + +### abs, fract, sign + +#### abs + +Returns the absolute value. + +#### fract + +Returns only the fractional part of a value. + +#### sign + +Sign function, will return 1 for positive values and -1 for negative values. + +### Trigonometic functions + +The following are included: `sin`, `cos`, `tan`, `asin`, `acos`, `atan` + +### Exponential and rational functions + +The following are included: `exp`, `log`, `exp2`, `log2`, `sqrt`, `inversesqrt` \ No newline at end of file diff --git a/doc/hydra-arrays.md b/doc/hydra-arrays.md new file mode 100644 index 0000000..95b4b50 --- /dev/null +++ b/doc/hydra-arrays.md @@ -0,0 +1,67 @@ +# hydra-arrays + +Extends the functionality of arrays in Hydra, letting you operate between different arrays and generate new ones. + +## Operators + +### Regular operators + +`.add`, `.sub`, `.mult`, `.div`, `.mod` + +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: + +``` +[0,1,2,3].add(1) == [1,2,3,4] +[0,1,2,3].add([1,2]) == [1,3,2,3] +``` + +### Wrap operators + +`.addWrap`, `.subWrap`, `.multWrap`, `.divWrap`, `.modWrap` + +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. + +``` +[0,1,2,3].add([1,2]) == [1,3,2,3] +[0,1,2,3].addWrap([1,2]) == [1,3,3,5] +``` + +## Methods + +### shuffle + +`.shuffle()` + +Will return a shuffled version of any array. + +### zfill + +`.zfill(length, z = 0)` + +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. + +### rotate + +`.rotate(n)` / `.rot(n)` + +Will rotate the array by `n` steps. Works with negative values. + +## Generators + +### Array.random + +`Array.random(length = 10, min = 0, max = 1)` + +Generates a new array of a given length and fills it with random values between the range `min` to `max`. + +### Array.range + +`Array.range(start, end, step = 1)` + +Generates a new array of numbers starting from `start`, then each element increases by `step` until `end` (non-inclusive). + +### Array.run + +`Array.range(end = 10, step = 1)` + +Generates a new array of numbers starting from 0, then each element increses by `step` until `end` (non-inclusive). \ No newline at end of file diff --git a/doc/hydra-blend.md b/doc/hydra-blend.md new file mode 100644 index 0000000..5ff8d92 --- /dev/null +++ b/doc/hydra-blend.md @@ -0,0 +1,50 @@ +# hydra-blend + +A port of [glsl-blend](https://github.com/jamieowen/glsl-blend) to Hydra. + +## Overview + +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). + +Please read the [disclaimer about alpha here below](#important) + +## List of blending modes + +* darken +* multiply +* colorBurn +* linearBurn +* lighten +* screen +* colorDodge +* linearDodge +* overlay +* softLight +* hardLight +* vividLight +* linearLight +* pinLight +* hardMix +* difference +* exclusion +* subtract +* divide +* negation / negate +* add2 (i didn't want to replace the regular Hydra `add`) +* glow +* reflect +* phoenix + +## About the alpha channel + +`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 + +### Important + +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. + + 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. + +### Note + +If you're interested in alpha compositing inside Hydra, please join the discussion [here](https://github.com/hydra-synth/hydra-synth/issues/109) \ No newline at end of file diff --git a/doc/hydra-canvas.md b/doc/hydra-canvas.md new file mode 100644 index 0000000..6a830a7 --- /dev/null +++ b/doc/hydra-canvas.md @@ -0,0 +1,45 @@ +# hydra-canvas + +This extension provides several methods for modifying the appearance and behavior of a canvas element in a web page. + +## The canvas object + +canvas: A reference to the canvas element with a class of bg-black. This makes this extension only work within the hydra editor. + +## Methods + +### `setLinear()` + +Sets the imageRendering property of the canvas element to auto, which causes the browser to use a linear interpolation algorithm for scaling the image. + +### `setNearest()` + +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. + +### `setFullscreen(full=true)` + +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. + +### `setAlign(align='right')` + +Sets the text-align property of the parent element of the canvas element to the specified value. The default value is 'right'. + +### `setRelativeSize(ratio)` + +Sets the width and height of the canvas element to a specified ratio of the current width and height. + +## Examples + +```javascript +// Set the canvas to fill the available space in the parent element +canvas.setFullscreen(); + +// Set the canvas to use nearest neighbor interpolation for scaling +canvas.setNearest(); + +// Set the canvas to be 50% of its current size +canvas.setRelativeSize(0.5); + +// Set the parent element of the canvas to align its contents to the left +canvas.setAlign('left'); +``` diff --git a/doc/hydra-colorspaces.md b/doc/hydra-colorspaces.md new file mode 100644 index 0000000..33806ce --- /dev/null +++ b/doc/hydra-colorspaces.md @@ -0,0 +1,206 @@ +# hydra-colorspaces + +The hydra-colorspaces extension adds lots of functions to work with color in many different colorspaces. + +## Available colorspaces + +| Colorspace | Elements | Stands for | Range | Description | +|-----------------|----------|------------|--------|---------------| +| rgb | r,g,b,a | Red, Green, Blue, Alpha | [0;1] [0;1] [0;1] [0;1] | Good old RGBA +| cmyk | c,m,y,k | Cyan, Magenta, Yellow, Key | [0;1] [0;1] [0;1] [0;1] | Substractive colorspace used in print +| hsv | h,s,v | Hue, Saturation, Value (Brightness) | [0;1] [0;1] [0;1] | Cylindrical colorspace where saturation peaks when brightness is 1 +| hsl | h,s,l | Hue, Saturation, Lightness | [0;1] [0;1] [0;1] | Cylindrical colorspace where saturation peaks when lightness is 0.5 +| 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 +| yiq | y,i,q | Luminance, In-phase, Quadrature | [0;1] [-0.5;+0.5] [-0.5;+0.5] | Color model used in NTSC systems + +## List of functions + +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: + +```js +// solid +hsv(1,0.5,1,1).out() +// color +gradient().hsv(1,0.5,1).out() +gradient().hsv.offset(.5,0,-.2).out() +gradient().hsv.invert(1,0,1).out() +gradient().hsv.to().out() +gradient().hsv.from().out() +// element specific +gradient().hsv.h().out() +gradient().hsv.hMult(2).out() +gradient().hsv.hOffset(.2).out() +gradient().hsv.hInvert().out() +gradient().hsv.hSet(0.3).out() +gradient().hsv.hFrom(osc()).out() +gradient().hsv.hMultFrom(osc(),1.5).out() +gradient().hsv.hOffsetFrom(noise(),0.2).out() +gradient().hsv.hKey(0.2).out() +gradient().hsv.hWith(x=>x.pixelate()).out() +``` + +## Syntax + +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()`. + +### Arguments + +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. + +### About alpha + +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)`. + +--- + +## Functions + +### Solid colors + +**`cs( el = 0, alpha = 1 )`** +* Example: `hsv(()=>time,1,1).out()` +* Alias: `cs_solid()` + +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. + +--- + +### Multiply elements + +**`.cs( el = 1, alpha = 1 )`** +* Example: `osc(20,.1,2).yuv(1,1,0).out()` +* Alias: `.cs.color()`, `.cs_color()` + +These functions will act similarly to Hydra's `.color()` function, allowing you to multiply the value of each element in the colorspace. + +#### Multiply a specific element + +**`.cs.elMult( amt = 1 )`** +* Example: `gradient().hsl.sMult(.8).out()` +* Alias: `.cs_el_mult()` + +--- + +### Offset elements + +**`.cs.offset( el = 0, alpha = 0 )`** +* Example: `gradient().hsv.offset(.5,-.2,.5).out()` +* Alias: `.cs_offset()` + +Offset an element by a given amount. Basically adds the number you give it to the corresponding element. + +#### Offset a specific element + +**`.cs.elOffset()`** +* Example: `osc().yiq.qOffset(.2).out()` +* Alias: `.cs_el_offset()` + +--- + +### Invert elements + +**`.cs.invert( el = 1, alpha = 0 )`** +* Example: `gradient().cmyk.invert(1,0,1,0).out()` +* Alias: `.cs_invert()` + +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. + +#### Invert a specific element + +**`.cs.elInvert()`** +* Example: `osc(5,.1,2).yiq.qInvert().out()` +* Alias: `.cs_el_invert()` + +--- + +### Converting to a given colorspace + +**`.cs.to()`** +* Example: `gradient().cmyk.to().out()` +* Alias: `.cs_to()` + +Converts the color values of the texture which calls the method to the given colorspace. + +### Converting from a given colorspace to RGBA + +**`.cs.from()`** +* Example: `gradient().add(solid(0,0,1)).hsv.from().out()` +* Alias: `.cs_from()` + +Let's you convert back to RGBA, or interpret any color values as if they were from the given colorspace. + +--- + +## Other element specific functions + +### Retrieving an element + +**`.cs.el()`** +* Example: `osc(40,.1,Math.PI/2).hsv.h().out()` +* Alias: `.cs_el()` + +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. + +--- + +### Setting an element + +**`.cs.elSet( value = 1 )`** +* Example: `osc(8,.1,2).hsv.sSet(.5).out()` +* Alias: `.cs_el_set()` + +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. + +--- + +### Modulating an element by a given texture + +**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. + +#### Setting by a given texture + +**`.cs.elFrom( texture , amt = 1 )`** +* Example: `rgb(1,0,0).hsl.sFrom(osc(9)).out()` +* Alias: `.cs_el_from()` + +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. + +#### Multiplying by a given texture + +**`.cs.elMultFrom( texture, amt = 1 )`** +* Example: `gradient().hsl.sMultFrom(osc(9)).out()` +* Alias: `.cs_el_mult_from()` + +Will multiply the given element's value by the given texture. The texture's value will be multiplied by the `amt` parameter. + +#### Offsetting by a given texture + +**`.cs.elOffsetFrom( texture, amt = 1 )`** +* Example: `gradient().hsl.hOffsetFrom(noise(1,2),.2).out()` +* Alias: `.cs_el_mult_from()` + +Will add the texture to the given value. The texture's value will be multiplied by the `amt` parameter. + +--- + +### Keying by a given element + +**`.cs.elKey( topThreshold = 0.5 , topTolerance = 0.05 , bottomThreshold = 0 , bottomTolerance = 0 )`** +* Example: `solid().layer(gradient(1).rgb.rKey(.5,0)).out()` +* Alias: `.cs_el_key()` + +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. + +**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. + +--- + +### Applying a transform to a given element + +**`.cs.elWith( f )`** +* Example: `osc(10,.1,2).rotate().hsv.hWith(x=>x.pixelate(16,32)).out()` +* Alias: `.cs_el_with()` + +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. + +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. diff --git a/doc/hydra-debug.md b/doc/hydra-debug.md new file mode 100644 index 0000000..3d4e1de --- /dev/null +++ b/doc/hydra-debug.md @@ -0,0 +1,15 @@ +# hydra-debug + +Adds tools to make it easier to debug sketches. As of now there's only one tool, but it's a useful one! + +## Show fragment shader on screen + +### .debug() + +`osc().debug()` + +The `debug` function will open a pop up inside Hydra showing the resulting fragment shader of any hydra texture. + +`osc().debug(output)` + +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. \ No newline at end of file diff --git a/doc/hydra-fractals.md b/doc/hydra-fractals.md new file mode 100644 index 0000000..c46abb5 --- /dev/null +++ b/doc/hydra-fractals.md @@ -0,0 +1,57 @@ +# hydra-fractals + +Adds mirroring and other functions that can be useful for making fractals. You should also check [`hydra-wrap`](./hydra-wrap.md) for fractals. + +## Mirroring functions + +All mirroring functions are named after the axis which will be affected. + + +### mirrorX + +`.mirrorX( pos = 0 , coverage = 1 )` + +Result: `[1234|4321]` + +Leaves the image as is, up until `pos`, where it starts mirroring it. `coverage` is how long until the effect repeats itself. For example: + +`.mirrorX(0,0.5)` + +Result: `[12|21|12|21]` + +### mirrorY + +`.mirrorY( pos = 0 , coverage = 1 )` + +Same as `mirrorX` but on the other axis. + +### mirrorX2 + +`.mirrorX2( pos = 0 , coverage = 1 )` + +Result: `[8765|5678]` + +Same as `mirrorX` but mirrors from the other side. + +### mirrorY2 + +`.mirrorY2( pos = 0 , coverage = 1 )` + +Same as `mirrorY` but mirrors from the other side. + +### mirrorWrap + +`.mirrorWrap()` + +Will mirror any out of bounds coordinates. For example: + +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]` +Result: `[87654321] [12345678] [87654321]` + +## Other functions + +### inversion + +`.inversion()` + +Applies a circular inversion. \ No newline at end of file diff --git a/doc/hydra-gif.md b/doc/hydra-gif.md new file mode 100644 index 0000000..7074b5f --- /dev/null +++ b/doc/hydra-gif.md @@ -0,0 +1,26 @@ +# hydra-gif + +Load `.gif` files to Hydra sources. + +## Overview + +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. + +## Usage + +### .initGif() + +`s0.initGif(url, delay, params)` + +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. + +### s0.gif + +`s0.gif` + +This is the object representing the gif, here are some of its useful properties: + +* `s0.gif.delay` : change the delay in milliseconds from frame to frame +* `s0.gif.playSpeed` : change the play speed overall, you can set to negative values to go on reverse +* `s0.gif.width` +* `s0.gif.height` \ No newline at end of file diff --git a/doc/hydra-glsl.md b/doc/hydra-glsl.md new file mode 100644 index 0000000..edb371f --- /dev/null +++ b/doc/hydra-glsl.md @@ -0,0 +1,86 @@ +# hydra-glsl + +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. + +## Example + +```js +glsl('vec4(sin(((_st.x*54.)+time*2.)*vec3(0.1,0.102,0.101)),1.0)') + .diff(o0) + .glslColor('vec4(c0.brg,1.)') + .glslCoord('xy*=(1.0/vec2(i0, i0)); return xy',.25) + .glslCombine('return c0-c1',o1) + .glslCombineCoord('uv+(vec2(c0.r,c0.b)*0.1)',o1) + .out() +``` + +[Open in hydra!](https://hydra.ojack.xyz/?sketch_id=agiUw1vmrGmmf4Zy) + +--- + +## Functions + +| function | arguments after code | corresponding type | glsl arguments : aliases | +|--------------------------|------------------|--------------------|-----------------------------------| +| glsl() | ...args | 'src' | _st : st, uv, xy | +| osc().glslColor() | ...args | 'color' | _c0 : c0, color | +| osc().glslCoord() | ...args | 'coord' | _st : st, uv, xy | +| osc().glslCombine() | texture, ...args | 'combine' | _c0 : c0, color0;_c1: c1, color1 | +| osc().glslCombineCoord() | texture, ...args | 'combineCoord' | _st : st, uv, xy;_c0: c0, color | + +*Note that `osc()` is merely an example* + +### Extra functions + +| function | arguments after code | corresponding type | description | +|--------------------------|------------------|--------------------|-----------------------------------| +| 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`| + +--- + +## Arguments + +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]`. + +#### Important + +All inputs are `float` as of now. + +### Example + +```js +glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) + .glslColor('vec4(c0.brg-(sin(c0.b)*myVariable),1.)',['myVariable',0.2]) + .out() +``` + +You can even send arguments as functions in typical Hydra manner: + +```js +glsl('vec4(sin(uv.x*i0+(time*i1*vec3(i2,i2*2.,i2*3.))),1.0)',16,2,.3) + .glslColor('vec4(c0.brg-(sin(c0.b)*myVariable),1.)',['myVariable',()=>time%1]) + .out() +``` + +--- + +## About the code (and lazy code) + +* You can straight up use any of the aliases mentioned above. +* You can omit the semicolon at the end. +* You can omit the return keyword. +* You can use multiline code. + * You can only omit the semicolon in the last line. + * You can still omit the return keyword. +* 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. +* You cannot use aliases as names of custom arguments + +--- + +## Tip + +Have the browser's console open. Hydra doesn't show frag errors on the built-in console. + +## Note + +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. diff --git a/doc/hydra-mouse.md b/doc/hydra-mouse.md new file mode 100644 index 0000000..244e244 --- /dev/null +++ b/doc/hydra-mouse.md @@ -0,0 +1,23 @@ +# hydra-mouse + +This extension replaces the default mouse object used by Hydra, adding new properties. + +--- + +## Properties + +***Note:** In the next list, `mouse x y` means `mouse.x` and `mouse.y`* + +| properties | range / unit | relative to / absolute | +|--------------------|------------------|------------------------| +|`mouse x y` | pixels | browser's viewport | +|`mouse ax ay` | pixels | absolute | +|`mouse cx cy` | pixels | canvas (like p5) | +|`mouse rx ry` | [0; 1] | browser's viewport | +|`mouse crx cry` | [0; 1] | canvas | +|`mouse posx posy` | [0.5; -0.5] | browser's viewport | +|`mouse cposx cposy` | [0.5; -0.5] | canvas | + +### Note + +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. \ No newline at end of file diff --git a/doc/hydra-outputs.md b/doc/hydra-outputs.md new file mode 100644 index 0000000..04bb07d --- /dev/null +++ b/doc/hydra-outputs.md @@ -0,0 +1,55 @@ +# hydra-outputs + +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. + +### Example + +```js +o0.setNearest() +o1.setLinear() + +src(o0) + .layer(osc(30,.2,1).mask(shape(4,.1,0))) + .scale(1.01).rotate(.01) + .out(o0) + +src(o1) + .layer(osc(30,.2,1).mask(shape(4,.1,0))) + .scale(1.01).rotate(.01) + .out(o1) + +src(o0) + .layer(src(o1).mask(shape(1,0,0).rotate(Math.PI/2))) + .out(o2) + +render(o2) +``` + +[Open in Hydra!](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlkcmEtZXh0ZW5zaW9ucy5nbGl0Y2gubWUlMkZoeWRyYS1vdXRwdXRzLmpzJTIyKSUwQSUwQW8wLnNldE5lYXJlc3QoKSUwQW8xLnNldExpbmVhcigpJTBBJTBBc3JjKG8wKSUwQSUwOS5sYXllcihvc2MoMzAlMkMuMiUyQzEpLm1hc2soc2hhcGUoNCUyQy4xJTJDMCkpKSUwQSUwOS5zY2FsZSgxLjAxKS5yb3RhdGUoLjAxKSUwQSUwOS5vdXQobzApJTBBJTBBc3JjKG8xKSUwQSUwOS5sYXllcihvc2MoMzAlMkMuMiUyQzEpLm1hc2soc2hhcGUoNCUyQy4xJTJDMCkpKSUwQSUwOS5zY2FsZSgxLjAxKS5yb3RhdGUoLjAxKSUwQSUwOS5vdXQobzEpJTBBJTBBc3JjKG8wKSUwQSUwOS5sYXllcihzcmMobzEpLm1hc2soc2hhcGUoMSUyQzAlMkMwKS5yb3RhdGUoTWF0aC5QSSUyRjIpKSklMEElMDkub3V0KG8yKSUwQSUwQXJlbmRlcihvMik%3D) + +--- + +## How to use + +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. + +### Methods + +| Method | Description | +|---|---| +| o0.setLinear() | Sets the interpolation method to linear. Looks smooth. | +| o0.setNearest()| Sets the interpolation method to nearest neighbour. Looks pixelated. | +| 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. | + +* Remember you can change all outputs at the same time as such: `oS.setLinear()` +* You may want to use setFbos to set different interpolation methods for `mag` and `min`. For example: `oS.setFbos({ mag: 'linear', min: 'nearest' })` + +#### Wrapping methods + +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. + +| Method | Description | +|---|---| +| o0.setRepeat() | Sets the wrapping to repeat | +| o0.setMirror() | Sets the wrapping to mirror | +| o0.setClamp() | Sets the wrapping to clamp | diff --git a/doc/hydra-pixels.md b/doc/hydra-pixels.md new file mode 100644 index 0000000..771aac2 --- /dev/null +++ b/doc/hydra-pixels.md @@ -0,0 +1,78 @@ +# hydra-pixels + +--- +This extension adds a functionality to each Hydra output that allows you to read the content of the pixels displayed. + +## Example + +```js +osc(40,.09,1.5) + .diff(osc(20).luma()) +.out() + +oct = ()=> shape(8,.3).diff(shape(8,.28)).luma() +src(o0) + .layer( + oct() + .color(1,0,0) + .scale(()=>1+(pixel[0]/255)) + ) + .layer( + oct() + .color(0,0,1) + .scale(()=>1+(pixel[2]/255)) + ) + .layer(src(o0).scale(128).mask(shape(4,.03,0))) + .out(o1) + +render(o1) + +update = ()=> { + pixel = o0.read(width/2,height/2) // center of the screen +} +``` + +[open in hydra](https://hydra.ojack.xyz/?sketch_id=EOQUdowhyZmbRJIj) + +--- + +## Functions + +`Output` refers to a hydra output such as `o0`, `o1`, etc. + +### Output.read() + +`Output.read( x = 0, y = 0, w = 1, h = 1 )` + +* `x` :: x position from which to start reading +* `y` :: y position from which to start reading +* `w` :: width of the area to be read +* `y` :: height of the area to be read + +By default this function returns only the pixel at the position specified by the two first arguments. + +### Output.readAll() + +`Output.read()` + +Returns all pixels in the output. + +## How to use them + +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. + +### Making Hydra react to pixels + +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: + +```js +update = ()=> { + pixel = o0.read(innerWidth/2,innerHeight/2) // center of the screen +} +``` + +Now you can use `pixel` in your Hydra code. + +### Warning + +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. diff --git a/doc/hydra-swizzle.md b/doc/hydra-swizzle.md new file mode 100644 index 0000000..dc48f5e --- /dev/null +++ b/doc/hydra-swizzle.md @@ -0,0 +1,22 @@ +# hydra-swizzle + +This extensions replicates the [swizzling functionality from GLSL](https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Swizzling) in Hydra. + +## Example + +```js +gradient(1).rbg + .layer(osc(30,.1,2).bggr) + .layer(gradient().r.mask(shape(2))) + .out() +``` + +## Overview + +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. + +```js +texture().rgg // -> c0.rrga +texture().bgba // -> c0.bgba +texture().r // -> c0.rrra +``` diff --git a/doc/hydra-tap.md b/doc/hydra-tap.md new file mode 100644 index 0000000..cb77bf2 --- /dev/null +++ b/doc/hydra-tap.md @@ -0,0 +1,39 @@ +# hydra-tap + +A tap bpm control for Hydra. Inspired by Resolume's own tap. + +## Example + +```js +osc(30,.01,beats(1)).out() + +osc().rotate(beats_(2).curve(-3)).out() + +osc().scale(beats(1).curve(2).range(1,2)).out() + +// Ctrl + Space Bar for tapping +// Ctrl + , (Comma) for re-sync +``` +[open in hydra](https://hydra.ojack.xyz/?code=YXdhaXQlMjBsb2FkU2NyaXB0KCUyMmh0dHBzJTNBJTJGJTJGaHlkcmEtZXh0ZW5zaW9ucy5nbGl0Y2gubWUlMkZoeWRyYS10YXAuanMlMjIpJTBBJTBBb3NjKDMwJTJDLjAxJTJDYmVhdHMoMSkpLm91dCgpJTBBJTBBb3NjKCkucm90YXRlKGJlYXRzXygyKS5jdXJ2ZSgtMykpLm91dCgpJTBBJTBBb3NjKCkuc2NhbGUoYmVhdHMoMSkuY3VydmUoMikucmFuZ2UoMSUyQzIpKS5vdXQoKSUwQSUwQSUyRiUyRiUyMEN0cmwlMjAlMkIlMjBTcGFjZSUyMEJhciUyMGZvciUyMHRhcHBpbmclMEElMkYlMkYlMjBDdHJsJTIwJTJCJTIwJTJDJTIwKENvbW1hKSUyMGZvciUyMHJlLXN5bmM%3D) + +## Usage + +### Tap + +* `Ctrl + Space Bar` for tapping. +* `Ctrl + , (Comma)` for re-sync (resets time to 0). + +### Envelopes + +* `beats(n=1)`: A linear ramp from 1 down to 0 every `n` beats +* `beats_(n=1)`: A linear ramp from 0 up to 1 every `n` beats +* `beatsTri(n=1)`: Goes from 1 to 0 on `n` beats, and then back to 1 in the same time, creating a triangle wave. +* `beatsTri_(n=1)`: Same as avobe but inverted. + +#### Curve + +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. + +#### Range + +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))`. \ No newline at end of file diff --git a/doc/hydra-text.md b/doc/hydra-text.md new file mode 100644 index 0000000..d6e6af4 --- /dev/null +++ b/doc/hydra-text.md @@ -0,0 +1,52 @@ +# hydra-text + +A text source for Hydra + +## hydraText + +`hydraText` are the default settings for hydra-text, which you can change: + +```js +hydraText.font = "serif"; + +// these are the defaults: +window.hydraText = { + font: "sans-serif", // the font-family + fontStyle: "normal", // normal, bold, etc + fontSize: "auto", // must be either "auto", a percentage like "90%", or pixels like "100px" + textAlign: "center", // center, left or right + fillStyle: "white", // a css color, CanvasGradient or CanvasPattern + strokeStyle: "white", // a css color, CanvasGradient or CanvasPattern + lineWidth: "2%", // a percentage of the final fontSize + lineJoin: "miter", // miter, bevel or round + canvasResize: 2, // a factor by which to resize the canvas that displays the text + interpolation: "linear",// linear or nearest. represents the interpolation used inside hydra for the text's canvas + }; +``` + +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. + +## text + +`text( str, config )` + +* str: the text you want to write +* config: either an object with the properties you want to set, or a string with the font you want + +## strokeText + +`strokeText( str, config )` + +Same as `text` but draws the outline of the text instead + +## strokeFillText + +`strokeFillText( str, config )` + +Fill over stroke + +## fillStrokeText + +`fillStrokeText( str, config )` + +Stroke over fill \ No newline at end of file diff --git a/doc/hydra-vec4.md b/doc/hydra-vec4.md new file mode 100644 index 0000000..d3e1d7e --- /dev/null +++ b/doc/hydra-vec4.md @@ -0,0 +1,20 @@ +# hydra-vec4 + +Adds wrapper functions that allow you to construct vec4's like you would in GLSL. + +## Overview + +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. + +## Example + +```js +noise() + .mult( vec4( vec3(0.5) , 1 ) ) + .add( vec4( [0.5,0].smooth() ) ) + .layer( + vec4( vec3( [0, 1, 0.5] , vec2( ()=>time%1 ) ) , 1) + .mask(shape(4)) + ) + .out() +``` \ No newline at end of file diff --git a/doc/hydra-wrap.md b/doc/hydra-wrap.md new file mode 100644 index 0000000..9eb4da0 --- /dev/null +++ b/doc/hydra-wrap.md @@ -0,0 +1,28 @@ +# hydra-wrap + +Allows you to change how Hydra wraps textures, and control the wrapping of generators. + +## Overview + +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()`. + +--- + +## Functions + +You can access the different functions via the `hydraWrap` object. + +| Method | Description | +|-------------------|----------------------------------------------------| +| hydraWrap.setWrap() | Sets the wrapping on. This is the default. | +| hydraWrap.setNoWrap() | Disables wrapping. This means textures will clamp. | +| hydraWrap.setMirror() | Sets the wrapping to mirror. | +| 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. | + +As mentioned above, this extensions also adds a new coordinate function named `wrap` which will set any generator to wrap. See the following example: + +```js +shape().scale(.2).out(o0) +shape().wrap().scale(.2).out(o1) +render() +``` diff --git a/hydra-glsl.js b/hydra-glsl.js new file mode 100644 index 0000000..d0d0a99 --- /dev/null +++ b/hydra-glsl.js @@ -0,0 +1,171 @@ +//hydra-gsls +//code glsl on the fly inside Hydra code +//by RITCHSE +//docs: https://github.com/ritchse/hydra-extensions/blob/main/doc/hydra-glsl.md + +{ + const getHydra = function () { + const whereami = window.choo?.state?.hydra + ? "editor" + : window.atom?.packages + ? "atom" + : "idk"; + switch (whereami) { + case "editor": + return choo.state.hydra.hydra; + case "atom": + return global.atom.packages.loadedPackages["atom-hydra"] + .mainModule.main.hydra; + case "idk": + let _h = undefined; + _h = window._hydra?.regl ? window._hydra : _h; + _h = window.hydra?.regl ? window.hydra : _h; + _h = window.h?.regl ? window.h : _h; + _h = window.H?.regl ? window.H : _h; + _h = window.hy?.regl ? window.hy : _h; + return _h; + } + }; + window._hydra = getHydra(); + window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; +} + +{ + const gS = _hydraScope.osc().constructor.prototype; + const _glslExtension = { + inputArray: () => + new Array(10) + .fill("i") + .map((x, i) => ({ name: x + i, type: "float", default: 1 })), + nodeCount: 0, + checkCode: function (code) { + code = code.trim(); + let lines = code.split(";"); + let ll = lines.length - 1; + if (!lines[ll]) { + lines.pop(); + ll--; + } + lines[ll] = lines[ll].trim(); + lines[ll] = + "\n" + + (lines[ll].substring(0, 6) != "return" + ? "return " + lines[ll] + : lines[ll]) + + ";"; + code = lines.join(";"); + return code; + }, + getObjAndArgs: function (type, args) { + let inputArray = false; + if (args[0] instanceof Array && args[0][0].constructor === String) { + inputArray = this.inputArray().map((x, i) => { + x.name = args[i] ? args[i][0] : x.name; + return x; + }); + inputArray = inputArray.slice(0, args.length); + args = args.map((x) => x[1]); + } + let obj = { + name: "_glsl_ext_NODE_" + this.nodeCount, + type: type, + inputs: inputArray || this.inputArray(), + }; + this.nodeCount++; + return [obj, args]; + }, + glslSource: function (code, ...args) { + let prefix = [ + !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", + !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", + !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", + ].join(""); + let data = this.getObjAndArgs("src", args); + let obj = data[0]; + args = data[1]; + obj.glsl = prefix + this.checkCode(code); + _hydra.synth.setFunction(obj); + return globalThis[obj.name](...args); + }, + glslColor: function (self, code, ...args) { + let prefix = [ + !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", + !code.includes("vec4 color") ? "vec4 color = _c0;\n" : "", + ].join(""); + let data = this.getObjAndArgs("color", args); + let obj = data[0]; + args = data[1]; + obj.glsl = prefix + this.checkCode(code); + _hydra.synth.setFunction(obj); + return gS[obj.name].bind(self)(...args); + }, + glslHsv: function (self, code, ...args) { + code = "vec3 hsv = _rgbToHsv(c0.rgb);\n" + code; + code = code + "\nreturn vec4(_hsvToRgb(hsv),c0.a);"; + return this.glslColor(self, code, ...args); + }, + glslCoord: function (self, code, ...args) { + let prefix = [ + !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", + !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", + !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", + ].join(""); + let data = this.getObjAndArgs("coord", args); + let obj = data[0]; + args = data[1]; + obj.glsl = prefix + this.checkCode(code); + _hydra.synth.setFunction(obj); + return gS[obj.name].bind(self)(...args); + }, + glslCombine: function (self, code, texture, ...args) { + let prefix = [ + !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", + !code.includes("vec4 color0") ? "vec4 color0 = _c0;\n" : "", + !code.includes("vec4 c1") ? "vec4 c1 = _c1;\n" : "", + !code.includes("vec4 color1") ? "vec4 color1 = _c1;\n" : "", + ].join(""); + let data = this.getObjAndArgs("combine", args); + let obj = data[0]; + args = data[1]; + args.unshift(texture); + obj.glsl = prefix + this.checkCode(code); + _hydra.synth.setFunction(obj); + return gS[obj.name].bind(self)(...args); + }, + glslCombineCoord: function (self, code, texture, ...args) { + let prefix = [ + !code.includes("vec4 c0") ? "vec4 c0 = _c0;\n" : "", + !code.includes("vec4 color") ? "vec4 color = _c0;\n" : "", + !code.includes("vec2 uv") ? "vec2 uv = _st;\n" : "", + !code.includes("vec2 st") ? "vec2 st = _st;\n" : "", + !code.includes("vec2 xy") ? "vec2 xy = _st;\n" : "", + ].join(""); + let data = this.getObjAndArgs("combineCoord", args); + let obj = data[0]; + args = data[1]; + args.unshift(texture); + obj.glsl = prefix + this.checkCode(code); + _hydra.synth.setFunction(obj); + return gS[obj.name].bind(self)(...args); + }, + }; + + _hydraScope.glsl = _glslExtension.glslSource.bind(_glslExtension); + gS.glslColor = function (code, ...args) { + return _glslExtension.glslColor(this, code, ...args); + }; + gS.glslHsv = function (code, ...args) { + return _glslExtension.glslHsv(this, code, ...args); + }; + gS.glslCoord = function (code, ...args) { + return _glslExtension.glslCoord(this, code, ...args); + }; + gS.glslCombine = function (code, texture, ...args) { + return _glslExtension.glslCombine(this, code, texture, ...args); + }; + gS.glslBlend = gS.glslCombine; + gS.glslCombineCoord = function (code, texture, ...args) { + return _glslExtension.glslCombineCoord(this, code, texture, ...args); + }; + gS.glslModulate = gS.glslCombineCoord; +} diff --git a/hydra-strudel-ambar-testing.js b/hydra-strudel-ambar-testing.js new file mode 100644 index 0000000..0e246ff --- /dev/null +++ b/hydra-strudel-ambar-testing.js @@ -0,0 +1,45 @@ +// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ + +async function init() { + const strudel = await import("https://cdn.skypack.dev/@strudel.cycles/core"); + const webaudio = await import( + "https://cdn.skypack.dev/@strudel.cycles/webaudio" + ); + const mini = await import("https://cdn.skypack.dev/@strudel.cycles/mini"); + const { evalScope, controls } = strudel; + const { registerSynthSounds, initAudioOnFirstClick, webaudioScheduler } = + webaudio; + const { miniAllStrings } = mini; + const { src, shape, speed, ...otherControls } = strudel.controls; + //initAudioOnFirstClick(); + miniAllStrings(); + console.log(Object.keys(otherControls)); + const loadModules = evalScope( + evalScope, + otherControls, + strudel, + mini, + webaudio, + import("https://cdn.skypack.dev/@strudel.cycles/tonal") + ); + await Promise.all([loadModules, registerSynthSounds()]); + const scheduler = webaudioScheduler(); + Pattern.prototype.play = function () { + scheduler?.setPattern(this, true); + }; + + Pattern.prototype["value"] = function () { + return this.query( + new strudel.State(new strudel.TimeSpan(window.time, window.time)) + )[0].value; + }; + window.strudel = strudel + shush = () => scheduler?.stop(); + console.log("Strudel loaded!"); +} + +if (window.strudel === undefined) { + init(); +} + + diff --git a/hydra-strudel-olivia.js b/hydra-strudel-olivia.js new file mode 100644 index 0000000..415de5d --- /dev/null +++ b/hydra-strudel-olivia.js @@ -0,0 +1,46 @@ +// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ + +async function initHydraStrudel() { + const strudel = await import("https://cdn.skypack.dev/@strudel.cycles/core"); + const webaudio = await import( + "https://cdn.skypack.dev/@strudel.cycles/webaudio" + ); + const mini = await import("https://cdn.skypack.dev/@strudel.cycles/mini"); + const { evalScope, controls } = strudel; + const { registerSynthSounds, initAudioOnFirstClick, webaudioScheduler } = + webaudio; + const { miniAllStrings } = mini; + const { src, shape, speed, ...otherControls } = strudel.controls; + //initAudioOnFirstClick(); + miniAllStrings(); + console.log(Object.keys(otherControls)); + const loadModules = evalScope( + evalScope, + otherControls, + strudel, + mini, + webaudio, + import("https://cdn.skypack.dev/@strudel.cycles/tonal") + ); + await Promise.all([loadModules, registerSynthSounds()]); + const scheduler = webaudioScheduler(); + Pattern.prototype.play = function () { + scheduler?.setPattern(this, true); + }; + + Pattern.prototype["value"] = function () { + const t = scheduler.now() + return this.query( + new strudel.State(new strudel.TimeSpan(t, t)) + )[0].value; + }; + window.strudel = strudel + shush = () => scheduler?.stop(); + console.log("Strudel loaded!"); +} + +if (window.strudel === undefined) { + initHydraStrudel(); +} + + diff --git a/hydra-strudel.js b/hydra-strudel.js new file mode 100644 index 0000000..415de5d --- /dev/null +++ b/hydra-strudel.js @@ -0,0 +1,46 @@ +// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ + +async function initHydraStrudel() { + const strudel = await import("https://cdn.skypack.dev/@strudel.cycles/core"); + const webaudio = await import( + "https://cdn.skypack.dev/@strudel.cycles/webaudio" + ); + const mini = await import("https://cdn.skypack.dev/@strudel.cycles/mini"); + const { evalScope, controls } = strudel; + const { registerSynthSounds, initAudioOnFirstClick, webaudioScheduler } = + webaudio; + const { miniAllStrings } = mini; + const { src, shape, speed, ...otherControls } = strudel.controls; + //initAudioOnFirstClick(); + miniAllStrings(); + console.log(Object.keys(otherControls)); + const loadModules = evalScope( + evalScope, + otherControls, + strudel, + mini, + webaudio, + import("https://cdn.skypack.dev/@strudel.cycles/tonal") + ); + await Promise.all([loadModules, registerSynthSounds()]); + const scheduler = webaudioScheduler(); + Pattern.prototype.play = function () { + scheduler?.setPattern(this, true); + }; + + Pattern.prototype["value"] = function () { + const t = scheduler.now() + return this.query( + new strudel.State(new strudel.TimeSpan(t, t)) + )[0].value; + }; + window.strudel = strudel + shush = () => scheduler?.stop(); + console.log("Strudel loaded!"); +} + +if (window.strudel === undefined) { + initHydraStrudel(); +} + + diff --git a/hydra-strudela.js b/hydra-strudela.js new file mode 100644 index 0000000..0cd0a53 --- /dev/null +++ b/hydra-strudela.js @@ -0,0 +1,68 @@ +// licensed with CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ + +async function initHydraStrudel() { + const strudel = await import("https://cdn.skypack.dev/@strudel.cycles/core"); + const webaudio = await import( + "https://cdn.skypack.dev/@strudel.cycles/webaudio" + ); + const mini = await import("https://cdn.skypack.dev/@strudel.cycles/mini"); + const { evalScope, controls } = strudel; + const { registerSynthSounds, initAudioOnFirstClick, webaudioScheduler } = + webaudio; + const { miniAllStrings } = mini; + const { src, shape, speed, ...otherControls } = strudel.controls; + //initAudioOnFirstClick(); + miniAllStrings(); + console.log(Object.keys(otherControls)); + const loadModules = evalScope( + evalScope, + otherControls, + strudel, + mini, + webaudio, + import("https://cdn.skypack.dev/@strudel.cycles/tonal") + ); + await Promise.all([loadModules, registerSynthSounds()]); + const scheduler = webaudioScheduler(); + Pattern.prototype.play = function () { + scheduler?.setPattern(this, true); + }; + + Pattern.prototype["value"] = function () { + const t = scheduler.now() + return this.query( + new strudel.State(new strudel.TimeSpan(t, t)) + )[0].value; + }; + window.strudel = strudel + shush = () => scheduler?.stop(); + + document.addEventListener('keydown', function(event) { + console.log(event, event.key) + if (event.ctrlKey && event.key === '.') { + shush(); + } +}); + +// TODO We should load all the samples strudel loads by default and figure out how to lazy load the remaining ones +// see https://github.com/tidalcycles/strudel/blob/6a427d6f893c4380e312fbf68f216f05c04b0723/website/src/repl/prebake.mjs#L18 + await samples( + 'https://strudel.tidalcycles.org/tidal-drum-machines.json', + 'github:ritchse/tidal-drum-machines/main/machines/' + ); + await samples( + 'https://strudel.tidalcycles.org/EmuSP12.json', + 'https://strudel.tidalcycles.org/EmuSP12/' + ); + + console.log("Strudel loaded!"); + + + +} + +if (window.strudel === undefined) { + initHydraStrudel(); +} + + diff --git a/old/README.md b/old/README.md new file mode 100644 index 0000000..e2d97b1 --- /dev/null +++ b/old/README.md @@ -0,0 +1,6 @@ +# WARNING : deprecated + +Extensions in this folder are deprecated in favor of newer extensions. + +* hydra-blending-modes : use hydra-blend instead +* hydra-nowrap : use hydra-wrap and set wrapping to nowrap instead \ No newline at end of file diff --git a/old/doc/hydra-blending-modes.md b/old/doc/hydra-blending-modes.md new file mode 100644 index 0000000..2f2e499 --- /dev/null +++ b/old/doc/hydra-blending-modes.md @@ -0,0 +1,43 @@ +# hydra-blending-modes + +## **warning : deprecated !! use hydra-blend instead** + +Very simple but useful blending modes for Hydra. They all lack an intensity argument as of now. +Taken from [hydra-blocky](https://github.com/samarthgulati/hydra-blockly/). +Specifically [this file](https://github.com/samarthgulati/hydra-blockly/blob/master/image-editing-glsl-functions.js). + +See all available blending modes by evaluating: + +```js +> console.log(blendmodes_list) +``` + +```js +{ + 0: "darken", + 1: "multiply", + 2: "colorBurn", + 3: "linearBurn", + 4: "darkerColor", + 5: "lighten", + 6: "screen", + 7: "colorDodge", + 8: "linearDodge", + 9: "lighterColor", + 10: "overlay", + 11: "softLight", + 12: "hardLight", + 13: "vividLight", + 14: "linearLight", + 15: "pinLight", + 16: "hardMix", + 17: "difference", + 18: "exclusion", + 19: "subtract", + 20: "divide", + 21: "hueBlend", + 22: "colorBlend", + 23: "saturationBlend", + 24: "luminosityBlend" +} +``` diff --git a/old/hydra-blending-modes.js b/old/hydra-blending-modes.js new file mode 100644 index 0000000..406bc8d --- /dev/null +++ b/old/hydra-blending-modes.js @@ -0,0 +1,465 @@ +// blend modes +// https://www.shadertoy.com/view/XdS3RW +// https://www.w3.org/TR/compositing-1 +// https://raw.githubusercontent.com/samarthgulati/hydra-blockly/master/image-editing-glsl-functions.js + +console.log("hydra-blending-modes is deprecated, I recomment using hydra-blend instead") + +var blendmodes_glsl_fns = [ + // vec3 darken( vec3 s, vec3 d ) + // { + // return min(s,d); + // } + { + name: "darken", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(min(_c0a,_c1a), _c0.a);` + }, + + // vec3 multiply( vec3 s, vec3 d ) + // { + // return s*d; + // } + { + name: "multiply", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a * _c1a, _c0.a);` + }, + + // vec3 colorBurn( vec3 s, vec3 d ) + // { + // return 1.0 - (1.0 - d) / s; + // } + { + name: "colorBurn", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(1.0 - (1.0 - _c1a) / _c0a, _c0.a);` + }, + + // vec3 linearBurn( vec3 s, vec3 d ) + // { + // return s + d - 1.0; + // } + { + name: "linearBurn", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a + _c1a - 1.0, _c0.a);` + }, + + // vec3 darkerColor( vec3 s, vec3 d ) + // { + // return (s.x + s.y + s.z < d.x + d.y + d.z) ? s : d; + // } + { + name: "darkerColor", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return (_c0a.r + _c0a.g + _c0a.b < _c1a.r + _c1a.g + _c1a.b) ? vec4(_c0a, _c0.a) : vec4(_c1a, _c0.a);` + }, + + // vec3 lighten( vec3 s, vec3 d ) + // { + // return max(s,d); + // } + { + name: "lighten", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(max(_c0a,_c1a), _c0.a);` + }, + + // vec3 screen( vec3 s, vec3 d ) + // { + // return s + d - s * d; + // } + { + name: "screen", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a + _c1a - _c0a * _c1a, _c0.a);` + }, + + // vec3 colorDodge( vec3 s, vec3 d ) + // { + // return d / (1.0 - s); + // } + { + name: "colorDodge", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return _c0a == vec3(0.) ? vec4(0.) : _c1a == vec3(1.) ? vec4(1.) : vec4(_c1a / (1.0 - _c0a), _c0.a);` + }, + + // vec3 linearDodge( vec3 s, vec3 d ) + // { + // return s + d; + // } + { + name: "linearDodge", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a + _c1a, _c0.a);` + }, + + // vec3 lighterColor( vec3 s, vec3 d ) + // { + // return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; + // } + { + name: "lighterColor", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return (_c0a.r + _c0a.g + _c0a.b > _c1a.r + _c1a.g + _c1a.b) ? vec4(_c0a, _c0.a) : vec4(_c1a, _c0.a);` + }, + + // float overlay( float s, float d ) + // { + // return (d < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); + // } + // vec3 overlay( vec3 s, vec3 d ) + // { + // vec3 c; + // c.x = overlay(s.x,d.x); + // c.y = overlay(s.y,d.y); + // c.z = overlay(s.z,d.z); + // return c; + // } + { + name: "overlay", + type: "combine", + inputs: [], + glsl: ` vec4 o; + vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + o.r = (_c1a.r < 0.5) ? 2.0 * _c0a.r * _c1a.r : 1.0 - 2.0 * (1.0 - _c0a.r) * (1.0 - _c1a.r); + o.g = (_c1a.g < 0.5) ? 2.0 * _c0a.g * _c1a.g : 1.0 - 2.0 * (1.0 - _c0a.g) * (1.0 - _c1a.g); + o.b = (_c1a.b < 0.5) ? 2.0 * _c0a.b * _c1a.b : 1.0 - 2.0 * (1.0 - _c0a.b) * (1.0 - _c1a.b); + o.a = _c0.a; + return o;` + }, + + // float softLight( float s, float d ) + // { + // return (s < 0.5) ? d - (1.0 - 2.0 * s) * d * (1.0 - d) + // : (d < 0.25) ? d + (2.0 * s - 1.0) * d * ((16.0 * d - 12.0) * d + 3.0) + // : d + (2.0 * s - 1.0) * (sqrt(d) - d); + // } + // vec3 softLight( vec3 s, vec3 d ) + // { + // vec3 c; + // c.x = softLight(s.x,d.x); + // c.y = softLight(s.y,d.y); + // c.z = softLight(s.z,d.z); + // return c; + // } + { + name: "softLight", + type: "combine", + inputs: [], + glsl: ` vec4 c; + vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + 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); + 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); + 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); + c.a = _c0.a; + //(_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); + return c;` + }, + + // float hardLight( float s, float d ) + // { + // return (s < 0.5) ? 2.0 * s * d : 1.0 - 2.0 * (1.0 - s) * (1.0 - d); + // } + + // vec3 hardLight( vec3 s, vec3 d ) + // { + // vec3 c; + // c.x = hardLight(s.x,d.x); + // c.y = hardLight(s.y,d.y); + // c.z = hardLight(s.z,d.z); + // return c; + // } + + { + name: "hardLight", + type: "combine", + inputs: [], + glsl: ` vec4 c; + vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + c.r = (_c0a.r < 0.5) ? 2.0 * _c0a.r * _c1a.r : 1.0 - 2.0 * (1.0 - _c0a.r) * (1.0 - _c1a.r); + c.g = (_c0a.g < 0.5) ? 2.0 * _c0a.g * _c1a.g : 1.0 - 2.0 * (1.0 - _c0a.g) * (1.0 - _c1a.g); + c.b = (_c0a.b < 0.5) ? 2.0 * _c0a.b * _c1a.b : 1.0 - 2.0 * (1.0 - _c0a.b) * (1.0 - _c1a.b); + c.a = _c0.a; + return c;` + }, + + // float vividLight( float s, float d ) + // { + // return (s < 0.5) ? 1.0 - (1.0 - d) / (2.0 * s) : d / (2.0 * (1.0 - s)); + // } + + // vec3 vividLight( vec3 s, vec3 d ) + // { + // vec3 c; + // c.x = vividLight(s.x,d.x); + // c.y = vividLight(s.y,d.y); + // c.z = vividLight(s.z,d.z); + // return c; + // } + + { + name: "vividLight", + type: "combine", + inputs: [], + glsl: ` vec4 c; + vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + c.r = (_c0a.r < 0.5) ? 1.0 - (1.0 - _c1a.r) / (2.0 * _c0a.r) : _c1a.r / (2.0 * (1.0 - _c0a.r)); + c.g = (_c0a.g < 0.5) ? 1.0 - (1.0 - _c1a.g) / (2.0 * _c0a.g) : _c1a.g / (2.0 * (1.0 - _c0a.g)); + c.b = (_c0a.b < 0.5) ? 1.0 - (1.0 - _c1a.b) / (2.0 * _c0a.b) : _c1a.b / (2.0 * (1.0 - _c0a.b)); + c.a = _c0.a; + return c;` + }, + + // vec3 linearLight( vec3 s, vec3 d ) + // { + // return 2.0 * s + d - 1.0; + // } + + { + name: "linearLight", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(2.0 * _c0a + _c1a - 1.0, _c0.a);` + }, + + // float pinLight( float s, float d ) + // { + // return (2.0 * s - 1.0 > d) ? 2.0 * s - 1.0 : (s < 0.5 * d) ? 2.0 * s : d; + // } + + // vec3 pinLight( vec3 s, vec3 d ) + // { + // vec3 c; + // c.x = pinLight(s.x,d.x); + // c.y = pinLight(s.y,d.y); + // c.z = pinLight(s.z,d.z); + // return c; + // } + + { + name: "pinLight", + type: "combine", + inputs: [], + glsl: ` vec4 c; + vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + 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; + 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; + 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; + c.a = _c0.a; + return c;` + }, + + // vec3 hardMix( vec3 s, vec3 d ) + // { + // return floor(s + d); + // } + + { + name: "hardMix", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(floor(_c0a + _c1a), _c0.a);` + }, + + // vec3 difference( vec3 s, vec3 d ) + // { + // return abs(d - s); + // } + + { + name: "difference", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(abs(_c1a - _c0a), _c0.a);` + }, + + // vec3 exclusion( vec3 s, vec3 d ) + // { + // return s + d - 2.0 * s * d; + // } + { + name: "exclusion", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a + _c1a - 2.0 * _c0a * _c1a, _c0.a);` + }, + + // vec3 subtract( vec3 s, vec3 d ) + // { + // return s - d; + // } + { + name: "subtract", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a - _c1a, _c0.a);` + }, + + // vec3 divide( vec3 s, vec3 d ) + // { + // return s / d; + // } + { + name: "divide", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + return vec4(_c0a / _c1a, _c0.a);` + }, + + // // rgb<-->hsv functions by Sam Hocevar + // // http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl + // vec3 rgb2hsv(vec3 c) + // { + // vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + // vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + // vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + // float d = q.x - min(q.w, q.y); + // float e = 1.0e-10; + // return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); + // } + + // vec3 hsv2rgb(vec3 c) + // { + // vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + // vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + // return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + // } + + // vec3 hue( vec3 s, vec3 d ) + // { + // d = rgb2hsv(d); + // d.x = rgb2hsv(s).x; + // return hsv2rgb(d); + // } + { + name: "hueBlend", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + _c1a = _rgbToHsv(_c1a); + _c1a.x = _rgbToHsv(_c0a).x; + return vec4(_hsvToRgb(_c1a), _c0.a);` + }, + + // vec3 color( vec3 s, vec3 d ) + // { + // s = rgb2hsv(s); + // s.z = rgb2hsv(d).z; + // return hsv2rgb(s); + // } + + { + name: "colorBlend", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + _c0a = _rgbToHsv(_c0a); + _c0a.z = _rgbToHsv(_c1a).z; + return vec4(_hsvToRgb(_c0a), _c0.a);` + }, + + // vec3 saturation( vec3 s, vec3 d ) + // { + // d = rgb2hsv(d); + // d.y = rgb2hsv(s).y; + // return hsv2rgb(d); + // } + + { + name: "saturationBlend", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + _c1a = _rgbToHsv(_c1a); + _c1a.y = _rgbToHsv(_c0a).y; + return vec4(_hsvToRgb(_c1a), _c0.a);` + }, + + // vec3 luminosity( vec3 s, vec3 d ) + // { + // float dLum = dot(d, vec3(0.3, 0.59, 0.11)); + // float sLum = dot(s, vec3(0.3, 0.59, 0.11)); + // float lum = sLum - dLum; + // vec3 c = d + lum; + // float minC = min(min(c.x, c.y), c.z); + // float maxC = max(max(c.x, c.y), c.z); + // if(minC < 0.0) return sLum + ((c - sLum) * sLum) / (sLum - minC); + // else if(maxC > 1.0) return sLum + ((c - sLum) * (1.0 - sLum)) / (maxC - sLum); + // else return c; + // } + { + name: "luminosityBlend", + type: "combine", + inputs: [], + glsl: ` vec3 _c0a = _c0.rgb * _c0.a; + vec3 _c1a = _c1.rgb * _c1.a; + float dLum = dot(_c1a, vec3(0.3, 0.59, 0.11)); + float sLum = dot(_c0a, vec3(0.3, 0.59, 0.11)); + float lum = sLum - dLum; + vec3 c = _c1a + lum; + float minC = min(min(c.x, c.y), c.z); + float maxC = max(max(c.x, c.y), c.z); + if(minC < 0.0) return vec4(sLum + ((c - sLum) * sLum) / (sLum - minC), _c0.a); + else if(maxC > 1.0) return vec4(sLum + ((c - sLum) * (1.0 - sLum)) / (maxC - sLum), _c0.a); + else return vec4(c, _c0.a);` + } +]; + +blendmodes_glsl_fns.forEach((fn) => {setFunction(fn)}) +blendmodes_list = blendmodes_glsl_fns.map(a => a.name) + +console.log(blendmodes_list) diff --git a/old/hydra-nowrap.js b/old/hydra-nowrap.js new file mode 100644 index 0000000..1a44368 --- /dev/null +++ b/old/hydra-nowrap.js @@ -0,0 +1,135 @@ +const fboSettings = Array(2).fill({ + mag: "nearest", + min: "nearest", + width: width, + height: height, + wrapS: 'clamp', + wrapT: 'clamp', + format: "rgba" +}); +choo.state.hydra.hydra.o.forEach((output) => { + output.fbos = fboSettings.map((x) => + output.regl.framebuffer({ + color: output.regl.texture(x), + depthStencil: false, + }) + ); +}); + +[ + { + name: "prev", + type: "src", + inputs: [], + glsl: `return texture2D(prevBuffer, _st);`, + }, + { + name: "src", + type: "src", + inputs: [ + { + type: "sampler2D", + name: "tex", + default: NaN, + }, + ], + glsl: `return texture2D(tex, _st);`, + }, + { + name: "scroll", + type: "coord", + inputs: [ + { + type: "float", + name: "scrollX", + default: 0.5, + }, + { + type: "float", + name: "scrollY", + default: 0.5, + }, + { + type: "float", + name: "speedX", + default: 0, + }, + { + type: "float", + name: "speedY", + default: 0, + }, + ], + glsl: `_st.x += scrollX + time*speedX; _st.y += scrollY + time*speedY; return _st;`, + }, + { + name: "scrollX", + type: "coord", + inputs: [ + { + type: "float", + name: "scrollX", + default: 0.5, + }, + { + type: "float", + name: "speed", + default: 0, + }, + ], + glsl: `_st.x += scrollX + time*speed; return _st;`, + }, + { + name: "modulateScrollX", + type: "combineCoord", + inputs: [ + { + type: "float", + name: "scrollX", + default: 0.5, + }, + { + type: "float", + name: "speed", + default: 0, + }, + ], + glsl: `_st.x += _c0.r*scrollX + time*speed; return _st;`, + }, + { + name: "scrollY", + type: "coord", + inputs: [ + { + type: "float", + name: "scrollY", + default: 0.5, + }, + { + type: "float", + name: "speed", + default: 0, + }, + ], + glsl: `_st.y += scrollY + time*speed; return _st;`, + }, + { + name: "modulateScrollY", + type: "combineCoord", + inputs: [ + { + type: "float", + name: "scrollY", + default: 0.5, + }, + { + type: "float", + name: "speed", + default: 0, + }, + ], + glsl: ` _st.y += _c0.r*scrollY + time*speed; return _st;`, + }, +].forEach((x) => setFunction(x)); + +console.log("hydra-nowrap is deprecated, I recomment using hydra-wrap and setting the wrapping to nowrap") \ No newline at end of file diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..0cc4641 --- /dev/null +++ b/utils/README.md @@ -0,0 +1,65 @@ +# utility scripts + +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: + +## Find the HydraRenderer object + +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. + +```js +{ + const getHydra = function () { + const whereami = window.choo?.state?.hydra + ? "editor" + : window.atom?.packages + ? "atom" + : "idk"; + switch (whereami) { + case "editor": + return choo.state.hydra.hydra; + case "atom": + return global.atom.packages.loadedPackages["atom-hydra"] + .mainModule.main.hydra; + case "idk": + let _h = undefined; + _h = window._hydra?.regl ? window._hydra : _h; + _h = window.hydra?.regl ? window.hydra : _h; + _h = window.h?.regl ? window.h : _h; + _h = window.H?.regl ? window.H : _h; + _h = window.hy?.regl ? window.hy : _h; + return _h; + } + }; + window._hydra = getHydra(); + window._hydraScope = _hydra.sandbox.makeGlobal ? window : _hydra.synth; +} +``` + +Tip: Always call `setFunction` from `_hydra.synth.setFunction` + +## Get prototypes + +```js +const gS = _hydraScope.osc().constructor.prototype; // GlslSource prototype +const oP = _hydra.o[0].constructor.prototype; // Output prototype +const hS = _hydra.s[0].constructor.prototype; // HydraSource prototype +``` + +## Custom update chain + +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. + +```js +{ + if (!window._updateChain) { + window.update = window.update || ((dt) => { }); + window._updateChain = [() => window["update"]()]; + _hydra.sandbox.userProps = ["speed", "bpm", "fps"]; + _hydra.synth.update = (dt) => { + for (func of window._updateChain) { + func(dt); + } + }; + } +} +```