JavaScript sine wave

14,507

Solution 1

The basic sine function is defined as:

f(x) = A sin(wt + p)

where

  • A is the amplitude
  • w is the frequency
  • p is the phase

These factors determine how the graph of f will look like.

The amplitude can be thought of as a scaling factor, the larger A, the larger (absolute values) the peaks and lows of f.

The frequency determines how fast the sine function will run through all its values until it starts over again - sine is a periodic function. The larger k, the faster f will run through one period.

p is the phase, think of it as "shifting" the starting point of the function to the right (positive p) or left (negative). Hard to explain in words, have a look here for graphs.

The function you give in your example is a generalized version of

f: R->R², f(t)=(sin(t), cos(t))

Which is (one of) the parametrizations of the unit circle . If you increase t monotonously and plot x (sin(t)) and y (cos(t)) you will have a point flying on a circle with radius 1.

Your generalized function is

f: R->R², f(t) = (A sin(1/wt), A cos(1/wt)), w > 1

In your case A=ampl, t=top and w=20 for the x coordinate and w=25 for the y coordinate. These slight deviations for w are there make the movement jittery, so that it's no longer a perfect circle, but rather some "distorted" ellipse - snow flakes don't fall in perfect circles, I guess. Additionally this makes the path of the flake appear to be more random than straight perfect circles. It's an illusion though, this is all also very deterministic and still periodic - it's just that x and y movement are "out of phase" so it takes a whole lot longer until one period is completed.

w is chosen > 1 to "slow down" the circular movement. The larger you choose w, the lower the frequency will be and your moving point will complete a full circle much slower.

The larger you choose A, the larger your circle will become.

Solution 2

It just makes the sine wave bigger so the curves can be more easily observed.

Here's a fiddle I tried making. If I change 20 and 25 to 1, the movement gets less interesting. http://jsfiddle.net/AbM9z/1/

It would help to know what values the function is being called with.

Share:
14,507

Related videos on Youtube

DrStrangeLove
Author by

DrStrangeLove

Updated on June 04, 2022

Comments

  • DrStrangeLove
    DrStrangeLove almost 2 years
    track : function(x, y, top, ampl) {
            return {
                top : top + 2,
                x   : x + ampl * Math.sin(top / 20),
                y   : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
            };
        }
    

    This routine sends snowflakes flying in sine wave manner.

    But how does it do that? Please explain.

    It uses Math.sin for x; and Math.cos for y, but other snippets I've seen use them in the opposite way. Why? Why exactly top/20 and top/25?

    The whole code:

    <script type="text/javascript">
    var snowflakes = { // Namespace
        /* Settings */
    
        pics : [
    
            ['snow.gif' , 24, 24],
            ['snow2.gif', 24, 24],
            ['snow3.gif', 24, 24]
        ],
    
        track : function(x, y, top, ampl) {
            return {
                top : top + 2,
                x   : x + ampl * Math.sin(top / 20),
                y   : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
            };
        },
    
        quantity : 30,
    
        minSpeed : 20, // 1 - 100, minSpeed <= maxSpeed
    
        maxSpeed : 40, // 1 - 100, maxSpeed >= minSpeed
    
        isMelt : true, // true OR false
        /* Properties */
        screenWidth : 0,
        screenHeight : 0,
        archive : [],
        timer : null,
        /* Methods */
        addHandler : function(object, event, handler, useCapture) {
            if (object.addEventListener) object.addEventListener(event, handler, useCapture);
            else if (object.attachEvent)object.attachEvent('on' + event, handler);
            else object['on' + event] = handler;
        },
        create : function(o, index) {
            var rand = Math.random();
            this.timer = null;
            this.o = o;
            this.index = index;
            this.ampl = 3 + 7*rand;
            this.type =  Math.round((o.pics.length - 1) * rand);
            this.width = o.pics[this.type][1];
            this.height = o.pics[this.type][2];
            this.speed = o.minSpeed + (o.maxSpeed - o.minSpeed) * rand;
            this.speed = 1000 / this.speed;
            this.deviation = o.maxDeviation * rand;
            this.x = o.screenWidth * rand - this.width;
            this.y = 0 - this.height;
            this.top = this.y;
            this.img = document.createElement('img');
            this.img.src = o.pics[this.type][0];
            this.img.style.top = this.y + 'px';
            this.img.style.position = 'absolute';
            this.img.style.zIndex = 10000;
            this.img.style.left = this.x + 'px';
            this.img.obj = this;
            if (o.isMelt) this.img.onmouseover = function() {
                clearTimeout(this.obj.timer);
                this.obj.timer = null;
                this.parentNode.removeChild(this);
            }
            document.body.appendChild(this.img);
            this.move();
        },
        init : function() {
            this.screenWidth = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth);
            this.screenWidth = navigator.userAgent.toLowerCase().indexOf('gecko') == -1 ? this.screenWidth : document.body.offsetWidth;
            this.screenHeight = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight);
            this.screenScroll = (window.scrollY) ? window.scrollY : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
            this.archive[this.archive.length] = new this.create(this, this.archive.length);
            clearTimeout(this.timer);
            this.timer = null
            this.timer = setTimeout(function(){snowflakes.init()}, 60000 / this.quantity);
        }
    };
    snowflakes.create.prototype = {
        move : function() {
            var newXY = this.o.track(this.x, this.y, this.top, this.ampl);
            this.x   = newXY.x;
            this.y   = newXY.y;
            this.top = newXY.top;
            if (this.y < this.o.screenHeight + this.o.screenScroll - this.height) {
                this.img.style.top  = this.y + 'px';
                this.x = this.x < this.o.screenWidth - this.width ? this.x : this.o.screenWidth - this.width;
                this.img.style.left = this.x + 'px';
                var index = this.index;
                this.timer = setTimeout(function(){snowflakes.archive[index].move()}, this.speed);
            } else {
                delete(this.o.archive[this.index]);
                this.img.parentNode.removeChild(this.img);
            }
        }
    };
    snowflakes.addHandler(window, 'load', function() {snowflakes.init();});
    snowflakes.addHandler(window, 'resize', function() {snowflakes.init();});
        </script>
    
  • david van brink
    david van brink over 12 years
    ampl makes it bigger, top/20 and top/25 change the wavelength, ya?
  • DrStrangeLove
    DrStrangeLove over 12 years
    Where did you change to 1?? In that fiddle i see top/20 and top/25
  • Gorm Casper
    Gorm Casper over 12 years
    I changed it but didn't save it. I should have made it toggle between the two. In fact, I think I'll do that right now.
  • Gorm Casper
    Gorm Casper over 12 years
    OK, I've got 'em side-by-side now.
  • DrStrangeLove
    DrStrangeLove over 12 years
    but why w = 5 and 4, i thought it was 20 and 25?? And why it's A sin(1/wt), A cos(1/wt), i thought it was A sin(top/20), A cos(top/25)?? and what is R->R²??
  • emboss
    emboss over 12 years
    LOL, that happens when it's late - I read top/20 as o.2*top, so w would have been 5... it's 20 and 25 of course, sorry for that confusion, I'll update that right away. R->R² is domain and range of the function - it takes values from R as input, the output is a two-dimensional vector (x,y) where both y and y are in R, so (x, y) is in RxR which is abbreviated as R² in mathematics.