Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Control flow primitives: if() #77

Closed
jeremydouglass opened this issue Feb 11, 2020 · 8 comments
Closed

Control flow primitives: if() #77

jeremydouglass opened this issue Feb 11, 2020 · 8 comments

Comments

@jeremydouglass
Copy link

It would be nice if hydra supported one or more control-flow primitives.

Just the single primitive if() (even without else) could be very powerful. Pseudocode example:

interactive multi-sketch selection:

if(mouse.x > 0.5) { render(o0) }
if({mouse.x < 0.5) { render(o1) }

or a cycling timed sketch sequence:

if(120 < time%360) { render(o0) }
if(240 < time%360) { render(o1) }
if(360 < time%360) { render(o2) }
@jeremydouglass jeremydouglass changed the title Control flow primitives Control flow primitives: if() Feb 11, 2020
@ojack
Copy link
Member

ojack commented Feb 12, 2020

Hydra does support these because it is just javascript..... or am I missing something?

@jeremydouglass
Copy link
Author

jeremydouglass commented Feb 13, 2020

Ah, I see. I was thrown off by reading the documentation list of commands and not finding any such keywords in there -- I thought those were the only keywords that would work in the web editor window. I see now the "Written in javascript and compatible with other javascript libraries" on the page https://hydra-editor.glitch.me/.

Perhaps I am being confused by an unrelated issue with the hydra web editor, specifically.

For context, here is a simple sketch in the p5.js web editor (https://editor.p5js.org/). Move mouse left for blue, right for red.

function setup() {
  createCanvas(400, 400);
}
function draw() {
  if(mouseX < 200) {
    background(0,0,255);
  } else {
    background(255,0,0);
  }
}

By comparison, I now tried putting this into the hydra editor (https://hydra-editor.glitch.me/):

if(mouse.x > 400) osc(8,-0.5, 1).out(o0)
if(mouse.x < 400) osc(8,  0.5, 1).out(o0)

Moving the mouse left and right does nothing. The screen does update based on the current mouse location each time I evaluate with Option-Enter, but it isn't reactive. Is this a specific limitation of the web editor, and would you recommend the Atom plugin or Electron app for this instead? Or am I not doing this in the right way?

@zachkrall
Copy link
Contributor

zachkrall commented Feb 13, 2020

@jeremydouglass

you can pass functions as parameters

example:

osc(
    8,
    () => {
        if( mouse.x > 400 ){
            return -0.5;
        } else {
            return 0.5;
        }
    },
    1
)
.out(o0)

and for brevity, you could use a ternary operator to change those values

example:

osc(
    8,
    () => mouse.x > 400 ? -0.5 : 0.5,
    1
)
.out(o0)

@jeremydouglass
Copy link
Author

Thanks, @zachkrall -- these are great examples of using if on embedded parameters with hydra.

Is it possible to use if for control flow?

For example, this does not work.

s0.initCam()
if(() => mouse.x > 400) osc(8,-0.5, 1).out(o0)
if(() => mouse.x < 400) src(s0).out(o0)

Although this does, just not with live updating:

s0.initCam()
if(mouse.x > 400) osc(8,-0.5, 1).out(o0)
if(mouse.x < 400) src(s0).out(o0)

@zachkrall
Copy link
Contributor

zachkrall commented Feb 13, 2020

I think that when you are evaluating those if statements it falls outside of the render loop that hydra uses so it only returns a value once.

https://github.com/ojack/hydra-synth/blob/5519e14198f9b367fcd09f15bbf54f851f5cc543/example/index.js#L59
https://github.com/ojack/hydra-synth/blob/5519e14198f9b367fcd09f15bbf54f851f5cc543/index.js#L300

@ojack
Copy link
Member

ojack commented Feb 13, 2020

Yes, exactly what @zachkrall says. You need to continuously call the function (i.e. what is happening in Processing's draw function) in order to continue to evaluate it. For example:

s0.initCam()

 update = () => {
   if(mouse.x > 90) osc(8,-0.5, 1).out(o0)
  if(mouse.x < 400) src(s0).out(o0)
  requestAnimationFrame(update)
 }

 update()

(In this example, make sure you only call update() once, otherwise you will be adding more and more continuously-called functions)

@ojack ojack closed this as completed Feb 13, 2020
@ojack
Copy link
Member

ojack commented Feb 13, 2020

Actually I should add a caveat to the above! It can be slower because the shader is re-compiled each time each chain of hydra functions is called. So I still would recommend finding a way to do things more idiomatically "hydra" because it will be better in terms of performance.

All of your examples can be recreated using multiple outputs and .blend(). For example,

s0.initCam()

osc(8,-0.5, 1)
  .blend(s0, () => mouse.x > 400 ? 0 : 1)
  .out(o0)

@jeremydouglass
Copy link
Author

Thanks for this, all. If I'm understanding the gist, it is:

  1. For control flow that executes once on eval of the editor code, use any normal JavaScript (if, else, etc.)
  • However, this is outside the hydra render loop -- it is not continuously evaluated
  1. To continuously evaluate, create a function, e.g. with arrow function syntax, then call it
  • However, this can be slower because the shader is re-compiled each time each chain of hydra functions
  1. To continuously evaluate with high performance, parameterize each chain, e.g. with .blend() using conditional arguments (e.g. time conditions or mouse conditions).
  • This way chain shaders are not recompiled.
  1. In general, don't wrap hydra chains in control flow for reactivity or performance. Instead, make all chains top-level and pass conditional functions as parameters into those chains.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants