function WebCamClass(url, imgId, fps, errorImage) {
  // variables

  this.url = url;               // url to fetch the cam from
  this.imageNode = $(imgId);    // image node to place the cam in
  this.backgroundNode;          // background image node for buffering
  this.running = false;         // flag if cam is running
  this.time = 0;                // time since last messure (use for calculating fps)
  this.startTime = 0;           // the time when new image is starting to fetch
  this.delay = 1;               // the delay between frames
  this.fps = 0;                 // the fps to go for
  this.initilized = false;      // flag if the cam has been initilized
  this.currentFps = 0;          // the current fps for the cam
  this.errorImage = errorImage; // url for the image to show on error
  this.errorHappend = false;    // flag if an error has happend
  this.errorCount = 0;          // count the number or errors
  this.errorOnPrevious = false; // flags if previous frames was error
  this.timerId = null;          // holds the id for the timer
  this.staticImg = false;       // if there currently is a static image shown


  // dummy functions to create events

  this.stopped          = function() { }
  this.started          = function() { }
  this.fpsChange        = function() { }
  this.newFrame         = function() { }
  this.currentFpsChange = function(fps) { }

  // functions

  this.setUrl = function(url) {
    this.url = url;
  }

  this.setFps = function(fps) {
    this.fps = fps;
    this.delay = 1000 / fps;

    // fire fps change event
    this.fpsChange();
  }

  this.start = function() {
    if (!this.initilized) {
      setTimeout((function() {this.init(this, true)}).bind(this), 10);
      return;
    }
    this.running = true;
    this.errorHappend = false;
    this.fetchImage();

    // fire started event
    this.started();
  }

  this.stop = function() {
    this.running = false;
    clearTimeout(this.timerId);
    this.timerId = null;
    // fire stopped event
    this.stopped();
    this.setCurrentFps(0);
  }

  this.fetchImage = function() {
    //this.startTime = (new Date).getTime();
    if (this.running) {
      var rand = Math.random();
      this.backgroundNode.src = this.url+'&uniq='+rand;
    }
  }

  this.setCurrentFps = function(fps) {
    var tmp = this.currentFps;
    this.currentFps = fps;
    if (tmp != fps) this.currentFpsChange(fps);
  }

  this.imageLoaded = function() {
    if (!this.staticImg) this.imageNode.src = this.backgroundNode.src;
    if (!this.errorHappend) {
      var time = this.time;
      this.time = (new Date).getTime();
      //this.currentFps = 1000 / (this.time - time);
      this.setCurrentFps(1000 / (this.time - time))
    } else {
      //this.currentFps = 0;
      this.setCurrentFps(0);
    }
    if (this.running) {
      this.timerId = setTimeout((function() {this.fetchImage()}).bind(this), this.delay);
    }

    // fire new frame event
    this.newFrame();
  }

  this.showStatic = function(source, ifError) {
    this.staticImg = true;

    if (ifError) {
      this.imageNode.onerror = (function() { this.imageNode.onerror = null; this.imageNode.src = ifError; }).bind(this);
    }

    this.imageNode.src = source;
  }

  this.removeStatic = function() {
    this.staticImg = false;
    this.imageNode.onerror = null;
    this.imageNode.src = this.backgroundNode.src;
  }

  this.error = function() {
    if (!this.errorOnPrevious) {
      this.errorCount = 0;
    }
    this.errorOnPrevious = true;
    this.errorCount++;
    if (this.errorCount > 4) {
      this.stop();
      this.errorHappend = true;
      this.imageNode.src = this.errorImage;
      var recheckDelay = 10000;
      setTimeout((function() {this.start()}).bind(this), recheckDelay);
    } else {
      this.imageLoaded();
    }
  }

  this.init = function(cam, start) {
    cam.backgroundNode = document.createElement('img');
    document.body.appendChild(cam.backgroundNode);
    Element.setStyle(cam.backgroundNode, { display: 'none' });

    cam.backgroundNode.onload = function() {
      cam.imageLoaded();
    }
    cam.backgroundNode.onerror = function() {
      cam.error();
    }
    cam.initilized = true;
    if (start) {
      cam.start();
    }
  }

  this.setFps(fps);

}