Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <html>
  <head>
    <title>Proximity Alert</title>
    <script src="//js.leapmotion.com/0.4.0/leap.js"></script>
    <script src="//js.leapmotion.com/plugins/0.1.0/leapjs-plugins-0.1.0.js"></script>
  </head>
  <body>
    <br/>
    <h1>Proximity Alert</h1>
    <h2 id=status>Connect your Leap</h2>
    <br/>
    <p>
      <br/>
      <b>Usage:</b> [<a href='javascript:void(0);' id=begin>Click to Unmute</a>]
      <br/>Move your hand slowly and carefully above your Leap.  Start from the center, and move out in one direction.  As you near the edge of the Interaction Box, beeping will begin gradually, speeding up until you hit the edge.  Outside of the box, while the hand is in view, a constant beep will sound.
      <br/>
      <br/>
    </p>
    
    <img src="https://developer.leapmotion.com/documentation/images/Leap_InteractionBox.png"/>
    
    <p>
      Read more in <a href="https://developer.leapmotion.com/documentation/python/api/Leap.InteractionBox.html" target=_blank>the docs</a>.  
      Also check out <a href="https://github.com/leapmotion/leapjs-plugins" target=_blank>LeapJS Plugins</a>.
    </p>
    
    <hr/>
    
    <ul>
      <li><a href="https://labs.leapmotion.com/proximity-alert-building-audio-feedback-with-a-new-leapjs-plugin" target=_blank>Blog Post</a></li>
      <li><a href="https://github.com/leapmotion/leapjs-plugins/tree/master/extras/proximity-alert">Source on Github</a></li>
    </ul>
  </body>
</html>


              
            
!

CSS

              
                body {
  font-family: "Myriad Pro", Myriad, "Helvetica Neue", Helvetica, Arial, sans-serif;
  line-height: 1.5em;
}
p {
 max-width: 600px; 
}

ul {
  
}
li {
  display: inline-block;
  padding-left: 1em;
}
              
            
!

JS

              
                # This plugin is available for anyone to use or contribute to at:
# https://github.com/leapmotion/leapjs-plugins/extra/proximity-alert


window.onload = () ->
  proximityAlertScope = {}
  (new Leap.Controller)
    .use('handEntry')
    .use('proximityAlert', proximityAlertScope)
    .on('frame', -> document.getElementById('status').innerHTML = 'Leap Connected')
    .connect()
  
  # mute button:
  beginControl = document.getElementById('begin')
  proximityAlertScope.setVolume(0)
  beginControl.onclick = ->
    proximityAlertScope.setVolume(1)
    beginControl.innerHTML = "Unmuted"

    


    
# scroll down to see plugin code...






# notes:
# http://patorjk.com/experiments/tones/
# http://it-ebooks.info/book/2072/
# https://www.html5rocks.com/en/tutorials/webaudio/games/
# http://en.wikipedia.org/wiki/Piano_key_frequencies
Leap.Controller.plugin 'proximityAlert', (scope = {})->
  scope.beepFrq ||= 1318.51 # E6
  scope.continuousFrq ||= 1396.91 # F6
  scope.waveType ||= 0 # 0 sine, 1 square, 2 sawtooth, 3 triange
  scope.beepDuration ||= (distance)-> Math.pow((0.7 - distance), 3) # this returns beep length as a function of proximity
  scope.minBeepDuration ||= 0.02 # when this threshold is crossed, the constant tone will play

  context = new webkitAudioContext()
  panner = context.createPanner()
  masterGain = context.createGain()
  masterGain.connect(context.destination)  
  panner.connect(masterGain)  
  
  # takes in a value between 0 and 1
  scope.setVolume = (value)->
    masterGain.gain.value = value
  

  # this is a wrapper funciton around the web audio api, taking care of some of the more fiddley bits,
  # such as the fact that osciallators can only be used once.
  oscillate = (freq, duration)->
    oscillator = context.createOscillator()
    oscillator.type = scope.waveType 
    oscillator.connect(panner)
    oscillator.frequency.value = freq
    oscillator.start(0)
    oscillator.stop(context.currentTime + duration) if duration
    oscillator


  playingUntil = undefined
  activeOscillator = undefined

  # Plays a tone for a specified amount of time
  playBeep = (freq, duration)->
    spacing = duration / 2

    # stop playing continuous
    if playingUntil == Infinity
      activeOscillator.stop(0)
      activeOscillator = null
    else if context.currentTime < playingUntil
      return

    activeOscillator = oscillate(freq, duration)
    playingUntil = context.currentTime + duration + spacing

  # Starts an un-ending tone 
  playContinuous = (freq) ->
    return if context.currentTime < playingUntil

    activeOscillator = oscillate(freq)
    activeOscillator.continuous = true # our own custom detail
    playingUntil = Infinity

  # Stops all noise
  silence = ->
    if activeOscillator && activeOscillator.continuous
      activeOscillator.stop(0)
      activeOscillator = undefined
      playingUntil = undefined


  # Takes in a a target number and a range
  # Returns how far the target is from the closest end of the range
  # e.g., 
  # distanceFromSegment( 1.5, [0,1]) == 0.5 
  # distanceFromSegment(-0.7, [0,1]) == 0.7 
  distanceFromSegment = (number, range) ->
    if number > range[1]
      return number - range[1]
    if number < range[0]
      return range[0] - number
    return false # inside the segment

  setPannerPosition = (hand)->
    panner.setPosition(
      hand.stabilizedPalmPosition[0] / 100, 
      hand.stabilizedPalmPosition[1] / 100, 
      hand.stabilizedPalmPosition[2] / 100
    )

  # Bind the 'handLost' event, which is given by the handEntry plugin.
  @on 'handLost', ->
    # for now, we don't have fancy multi-hand support.
    silence()


  {
    hand: (hand)->
      return unless iBox = hand.frame.interactionBox

      # normalizePoint returns an array of three numbers representing fractional position within the interaction box.
      # e.g., a point in the middle of the box would be [0.5,0.5,0.5], and one outside to the right could be [0.5,1.2,0.5]
      proximities = iBox.normalizePoint(hand.palmPosition)
      for proximity in proximities
        if (distance = distanceFromSegment(proximity, [0,1]))
          hand.proximity = true

          setPannerPosition(hand)
          duration = scope.beepDuration(distance)
          if duration < scope.minBeepDuration
            playContinuous(scope.continuousFrq)
          else
            playBeep(scope.beepFrq, duration)

          return # for now, only check one proximity at a time.

      silence()
  }



# make links function regularly in codepen
links = document.querySelectorAll("a")
i = 0

###while i < links.length
  console.log 'removing link listener'
  links[i].removeEventListener "click", __linkClick
  i++


  ###
              
            
!
999px

Console