Wiki2
ElmSignalDelay

Delayed Signals in Elm

The task was to see if I could create a signal (e.g. a stock) by taking historic values from the stock input signal delayed based on some random value.

Elm has delay, but it only takes a static value as input (and not a signal). One way is to create a set of pre-defined signals with different delays and select which signal to use (at each point in time) based on another signal. range (from Random), combine (from Signal) and lift2 could be used to achieve that.

Instead I made a similar demo appliction which combines a set of delayed signal, but use all of them to produce its output.

import Mouse
import Window

-- Take input signal, delay with n different delays of (n*td) and combine them
memory dt n m = map (\t -> delay (second*t*dt) m) [0..n] |> combine

-- Create a dot on the screen with screen size (w,h) at (x,y)
a_dot (w,h) (x,y) =
  let (dx,dy) = (toFloat x - toFloat w / 2, toFloat h / 2 - toFloat y)
  in  ( ngon 6 5  |> filled orange |> move (dx, dy) )

-- Take a signal which contins a list of (x,y) values and display them 
scene ms (w,h) = 
  let dots = map (a_dot (w,h)) ms
  in  collage w h dots

-- Output `scene` with mouse input run through memory as input
main = lift2 scene (memory 0.1 30 Mouse.position) Window.dimensions

To try it just enter the code in the following URL, press Compile and move the mouse in the right part of the screen.

http://elm-lang.org/try

Amazing what 8 rows of code can produce!

Even if this wasn’t the original idea this quite nicely show how a single signal can be delayed and combined in a single output.

The tunnel

Create a tunnel effect in the center of the window.

import Mouse
import Window

-- Basic square distances (when dt = 0)
distances = [ 2 .. 15 ]

-- sub-second time signal goes between 1 and 0
time = let t = (foldp (+) 0 (fps 30))
           sub_t t = let t' = inSeconds t
                     in  t' - (toFloat (floor t'))
       in  lift sub_t t

-- Show square at position (x,y) at distance d
showSq d (x,y) (w, h) = 
  let r = w*(1/d)               -- square height
      c = rgba 50 50 10 0.1     -- square color
      (x', y') = (x, (-1)*y)
  in  filled c (square <| r) |> move (x',y')

-- Show all square where distance is moved by dt (0-1)
scene dt pos (w,h) = 
  let ds = map (\d -> d - dt) distances
  in  collage w h <| map (\d -> showSq d pos (toFloat w, toFloat h)) ds

-- Combine dt, joystick and window signal to show scene
main = lift3 scene time (constant (0,0)) Window.dimensions

Modify solution so that old mouse location are memorized. The mouse location change the location of the closest square which then is propagated to squares backwards.

import Mouse
import Window

-- Basic distances to each square (when dt = 0)
distances = [ 2 .. 14 ]
n = 2  -- 1/n is the time diff between two squares

--Time signal which goes between 1/n and 0 repeatadely
time = let t = (foldp (+) 0 (fps 30))
           sub_t t = let t' = inSeconds t
                     in  (1/n) * (n*t' - (toFloat <| n*floor t'))
       in  lift sub_t t
       
-- Take input signal, delay with n different delays of (n*td) and combine them
memory dt n m = map (\t -> delay (second*t*dt) m) [0..n] |> combine

-- Mouse joystick. Postive right and below relative center
joystick = 
  let rel_center (w,h) (x,y) = (toFloat x-(toFloat w/2), toFloat y-(toFloat h/2)) 
  in  lift2 rel_center Window.dimensions Mouse.position

-- Show square at position (x,y) at distance d
showSq d (x,y) (w, h) = 
  let r = w*(1/d)                            -- square height
      c = rgba (round d*8) 20 100 0.15    -- square color
      (x', y') = (x, -y)
  in  filled c (square r) |> move (x',y')

-- Show all square where distance is moved by dt*n
scene dt js (w,h) = 
  let ds = map (\d -> d - dt*n) distances
      (w', h') = (toFloat w, toFloat h)
      squares = zipWith (\d j -> showSq d j (w',h')) ds js
  in  collage w h squares

joy_in_time = memory 0.1 (toFloat <| length distances) joystick

-- Combine dt, joystick and window signal to show scene
main = lift3 scene time joy_in_time Window.dimensions

References