diff --git a/animation/moving_circle.js b/animation/moving_circle.js
new file mode 100644
index 0000000000000000000000000000000000000000..a1402e43098fbd248d08a5b3f9b0713b01a4f02d
--- /dev/null
+++ b/animation/moving_circle.js
@@ -0,0 +1,118 @@
+var animations=[]
+
+animations.MovingCircle=function(output_canvas_id,opt_options) {
+  this.radius=opt_options&&opt_options.radius?opt_options.radius:5;
+
+  this.x0=opt_options&&opt_options.x0?opt_options.x0:5;
+  this.y0=opt_options&&opt_options.y0?opt_options.y0:5;
+
+  this.step_x=opt_options&&opt_options.step_x?opt_options.step_x:1;
+  this.step_y=opt_options&&opt_options.step_x?opt_options.step_y:0;
+
+  this.random=opt_options&&opt_options.random?true:false;
+
+  this.output_cvs=document.getElementById(output_canvas_id);
+  this.output_ctxt=this.output_cvs.getContext("2d");
+  this.fillColor=opt_options&&opt_options.fillColor?opt_options.fillColor:[255,0,0,255];
+
+}
+
+animations.MovingCircle.prototype.move=function() {
+  var local_dx=Math.round(this.step_x*(!this.random?1:Math.random()));
+  var local_dy=Math.round(this.step_y*(!this.random?1:Math.random()));
+
+  this.x0+=local_dx; this.y0+=local_dy;
+
+  if ((this.x0+this.radius)>this.output_cvs.width) {
+    this.x0=this.output_cvs.width-this.radius; this.step_x=-this.step_x;
+  } else if (this.x0 < this.radius ) {
+      this.x0=this.radius; this.step_x=-this.step_x;
+  }
+
+  if ((this.y0+this.radius) > this.output_cvs.height) {
+    this.y0=this.output_cvs.height-this.radius; this.step_y=-this.step_y;
+  } else if (this.y0 < this.radius ) {
+    this.y0=this.radius; this.step_y=-this.step_y;
+  }
+}
+
+animations.MovingCircle.prototype.draw=function() {
+  this.output_ctxt.fillStyle="rgba("+this.fillColor.join(",")+")";
+  this.output_ctxt.beginPath();
+  this.output_ctxt.arc(this.x0,this.y0,this.radius,0,2*Math.PI);
+  this.output_ctxt.fill();
+}
+animations.MovingCircle.prototype.animate=function() {
+  this.output_ctxt.beginPath();
+  this.output_ctxt.clearRect(this.x0-this.radius,this.y0-this.radius,this.radius*2,this.radius*2);
+  this.move();
+  this.draw();
+}
+
+animations.MovingCircleHChangingColor=function(output_canvas_id,opt_options) {
+  this.__proto__.__proto__=new animations.MovingCircle(output_canvas_id,opt_options);
+
+  this.fillColor0=opt_options&&opt_options.fillColor0?opt_options.fillColor0:[255,0,0,0];
+  this.fillColor1=opt_options&&opt_options.fillColor1?opt_options.fillColor1:[0,255,0,0];
+  this.fillColorNbSteps=opt_options&&opt_options.fillColorNbSteps?opt_options.fillColorNbSteps:5;
+  this.fillColorIncrement=[];
+  for (i=0;i<4;i++) {
+    this.fillColorIncrement[i]=(this.fillColor1[i]-this.fillColor0[i])/this.fillColorNbSteps;
+  };
+  this.fillColor=this.fillColor0;
+  this.fillColorStepCount=0;
+  this.fillColorIncrementSign=1;
+
+}
+
+animations.MovingCircleHChangingColor.prototype.move=function() {
+  this.__proto__.__proto__.move();
+
+  if (this.fillColorStepCount==this.fillColorNbSteps) {
+    this.fillColorIncrementSign=-this.fillColorIncrementSign;
+    this.fillColorStepCount=0;
+  };
+
+  for (i=0;i<4;i++) {
+    this.fillColor[i]+=this.fillColorIncrementSign*this.fillColorIncrement[i];
+  };
+  this.fillColorStepCount++;
+}
+
+animations.MovingCircleHChangingColorChangingBackground=function(output_canvas_id,opt_options) {
+  this.__proto__.__proto__=new animations.MovingCircleHChangingColor(output_canvas_id,opt_options);
+
+  this.bgColor0=opt_options&&opt_options.bgColor0?opt_options.bgColor0:[255,0,0,255];
+  this.bgColor1=opt_options&&opt_options.bgColor1?opt_options.bgColor1:[0,255,0,255];
+  this.bgColorNbSteps=opt_options&&opt_options.bgColorNbSteps?opt_options.bgColorNbSteps:5;
+  this.bgColorIncrement=[];
+  for (i=0;i<4;i++) {
+    this.bgColorIncrement[i]=(this.bgColor1[i]-this.bgColor0[i])/this.bgColorNbSteps;
+  };
+  this.bgColor=this.bgColor0;
+  this.bgColorStepCount=0;
+  this.bgColorIncrementSign=1;
+
+}
+
+animations.MovingCircleHChangingColorChangingBackground.prototype.move=function() {
+  this.__proto__.__proto__.move();
+
+  if (this.bgColorStepCount==this.bgColorNbSteps) {
+    this.bgColorIncrementSign=-this.bgColorIncrementSign;
+    this.bgColorStepCount=0;
+  };
+
+  for (i=0;i<4;i++) {
+    this.bgColor[i]+=this.bgColorIncrementSign*this.bgColorIncrement[i];
+  };
+  this.bgColorStepCount++;
+}
+
+animations.MovingCircleHChangingColorChangingBackground.prototype.animate=function() {
+  this.output_ctxt.fillStyle="rgba("+this.bgColor.join(",")+")";
+  this.output_ctxt.beginPath();
+  this.output_ctxt.fillRect(0,0,this.output_cvs.width,this.output_cvs.height);
+  this.move();
+  this.draw();
+}
diff --git a/data/red_circle.gif b/data/red_circle.gif
new file mode 100644
index 0000000000000000000000000000000000000000..660e416cf70e75545dfafb805ab084eae3a61cfe
Binary files /dev/null and b/data/red_circle.gif differ
diff --git a/data/red_circle.jpg b/data/red_circle.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..5af437be485f66ac069cc735fe2e2a76d0908a64
Binary files /dev/null and b/data/red_circle.jpg differ
diff --git a/data/red_circle.png b/data/red_circle.png
new file mode 100644
index 0000000000000000000000000000000000000000..908c1242aa2c135b1123925572820d65ae8d2ae7
Binary files /dev/null and b/data/red_circle.png differ
diff --git a/effects/focus.js b/effects/focus.js
new file mode 100644
index 0000000000000000000000000000000000000000..02a352599249b48c8b5877a4dda23c8fc2e75a05
--- /dev/null
+++ b/effects/focus.js
@@ -0,0 +1,117 @@
+var focus={}
+
+focus.MovingFocus = function(opt_options) {
+  this.focus_x=opt_options.focus_x;
+  this.focus_y=opt_options.focus_y;
+  this.focus_radius=opt_options.focus_radius;
+  this.focus_dx=opt_options&&opt_options.focus_dx?opt_options.focus_dx:this.focus_radius/10;
+  this.focus_dy=opt_options&&opt_options.focus_dy?opt_options.focus_dy:this.focus_radius/10;
+  this.width=opt_options.width;
+  this.height=opt_options.height;
+  this.random=opt_options&&opt_options.random?opt_options.random:false;
+}
+
+focus.MovingFocus.prototype.updateFocus=function() {
+  var increment_x=Math.round(this.focus_dx*(this.random?Math.random():1))
+  this.focus_x+=increment_x;
+
+  if ((this.focus_x+this.focus_radius>this.width)
+          || (this.focus_x-this.focus_radius<0)) {
+      this.focus_dx=-this.focus_dx;
+      this.focus_x+=2*this.focus_dx;
+
+  }
+
+  var increment_y=Math.round(this.focus_dy*(this.random?Math.random():1))
+  this.focus_y+=increment_y;
+  if ((this.focus_y+this.focus_radius>this.height)
+  ||(this.focus_y-this.focus_radius<0))
+  {
+      this.focus_dy=-this.focus_dy;
+      this.focus_y+=2*this.focus_dy;
+
+  }
+}
+
+focus.MovingFocus.prototype.isInsideFocus=function(i,j) {
+    if (Math.sqrt((i-this.focus_x)*(i-this.focus_x)+
+            (j-this.focus_y)*(j-this.focus_y))
+            <=this.focus_radius)
+        return true;
+    else
+        return false;
+}
+
+focus.MovingFocus.prototype.process = function(in_imgData,out_imgData) {
+  var self=this;
+  var w=0;
+  var count=0;
+  var pixels=in_imgData.data;
+
+  //console.log("track "+this.focus_x+"x"+this.focus_y);
+  for (var i = 0; i < in_imgData.height; i++) {
+    for (var j = 0; j < in_imgData.width; j++) {
+      var mean=(pixels[w+1]+pixels[w+2]+pixels[w])/3;
+
+      if (this.isInsideFocus(j,i)) {
+          var focus_i=i-this.focus_y;
+          var focus_j=j-this.focus_x;
+          var angleF=Math.atan2(focus_i,focus_j);
+
+          var radius=Math.round(Math.sqrt(focus_i*focus_i+focus_j*focus_j));
+          var angleR=Math.atan2(radius,this.focus_radius);
+
+          var cible_i=Math.round(this.focus_y+focus_i);
+          var cible_j=Math.round(this.focus_x+focus_j);
+
+          //var cible_i=Math.round(this.focus_y+
+          //                            radius*Math.sin(-angle));
+          //var cible_j=Math.round(this.focus_x+
+          //                             radius*Math.cos(-angle));
+
+          //var cible_i=Math.round(this.focus_y+
+          //                            radius*Math.sin(angle)*Math.sin(angle));
+          //var cible_j=Math.round(this.focus_x+
+          //                             radius*Math.cos(angle)*Math.cos(angle));
+
+          /*var cible_i=Math.round(this.focus_y+
+                                      radius*(Math.sin(angle*angle)));
+          var cible_j=Math.round(this.focus_x+
+                                       radius*(Math.cos(angle*angle)));
+          */
+
+          /*
+          var cible_i=Math.round(this.focus_y+
+                                      radius*(Math.sin(angleF)*Math.sin(angleR)));
+          var cible_j=Math.round(this.focus_x+
+                                       radius*(Math.cos(angleF)*Math.cos(angleR)));
+          */
+
+          /*var cible_i=Math.round(this.focus_y+
+                                      radius*(Math.sin(angleF)/Math.sin(angleR)));
+          var cible_j=Math.round(this.focus_x+
+                                       radius*(Math.cos(angleF)/Math.cos(angleR)));
+*/
+
+          /*var cible_i=Math.round(this.focus_y+
+                                      radius*radius/this.focus_radius
+                                      *Math.sin(angleF));
+          var cible_j=Math.round(this.focus_x+
+                                       radius*radius/this.focus_radius
+                                       *Math.cos(angleF));*/
+
+          var cible_w=(cible_i*in_imgData.width+cible_j)<<2;
+          //console.log(i+"x"+j+" "+w+" "+focus_i+"x"+focus_j+" - "+cible_i+"x"+cible_j+ " "+cible_w);
+          out_imgData.data[w+1]=pixels[cible_w+1];
+          out_imgData.data[w+2]=pixels[cible_w+2];
+          out_imgData.data[w]=pixels[cible_w];
+      } else {
+          out_imgData.data[w + 1]=mean * 1;
+          out_imgData.data[w + 2]=mean * 1;
+          out_imgData.data[ w ]=mean * 1;
+      }
+      out_imgData.data[w+3]=pixels[w+3];
+      w+=4;
+    }
+  }
+};
diff --git a/effects/generic.js b/effects/generic.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6d3fd9e0b7a9813f9639400f9aada1821084b4a
--- /dev/null
+++ b/effects/generic.js
@@ -0,0 +1,19 @@
+var generic_effect={}
+
+generic_effect.apply_region_filter=function(in_imgData,out_imgData,window_width,window_height,
+  pixel_form_region_rgba_filter) {
+
+  var w=0;
+
+  for (var y=0;y<out_imgData.height;y++)
+    for (var x=0;x<out_imgData.width;x++) {
+      w=((y*out_imgData.width)+x)<<2;
+      new_rgba_pixel_val=pixel_form_region_rgba_filter(
+          in_imgData,
+          x-Math.round(window_width/2), y-Math.round(window_height/2),
+          window_width, window_height);
+      for (var i=0;i<4;i++) {
+        out_imgData.data[w+i]=new_rgba_pixel_val[i];
+      }
+    }
+}
diff --git a/effects/morpho.js b/effects/morpho.js
new file mode 100644
index 0000000000000000000000000000000000000000..9a8fcb84399ab6971f56069c0dd61030caef6fae
--- /dev/null
+++ b/effects/morpho.js
@@ -0,0 +1,42 @@
+var morpho_effects={}
+
+morpho_effects.DilatationWindow = function(opt_options) {
+  this.window_width=opt_options.window_width;
+  this.window_height=opt_options.window_height;
+}
+
+morpho_effects.DilatationWindow.prototype.process=function(in_imgData,out_imgData) {
+  return generic_effect.apply_region_filter(
+      in_imgData,out_imgData,this.window_width,this.window_height,
+      morpho_filters.max_from_region
+    );
+}
+
+morpho_effects.ErosionWindow = function(opt_options) {
+  this.window_width=opt_options.window_width;
+  this.window_height=opt_options.window_height;
+}
+
+morpho_effects.ErosionWindow.prototype.process=function(in_imgData,out_imgData) {
+  generic_effect.apply_region_filter(
+      in_imgData,out_imgData,this.window_width,this.window_height,
+      morpho_filters.min_from_region);
+}
+
+morpho_effects.DilatationErosionWindow = function(opt_options) {
+  this.dilatation_width=opt_options.dilatation_width;
+  this.dilatation_height=opt_options.dilatation_height;
+  this.erosion_width=opt_options.erosion_width;
+  this.erosion_height=opt_options.erosion_height;
+  this.aux_cvs=document.createElement("canvas");
+}
+
+morpho_effects.DilatationErosionWindow.prototype.process=function(in_imgData,out_imgData) {
+  aux_imgData ={data:[],width:out_imgData.width,height:out_imgData.height};
+  generic_effect.apply_region_filter(
+      in_imgData,aux_imgData,this.dilatation_width,this.dilatation_height,
+      morpho_filters.max_from_region);
+  generic_effect.apply_region_filter(
+      aux_imgData,out_imgData,this.erosion_width,this.erosion_height,
+      morpho_filters.min_from_region);
+}
diff --git a/features/pixels.js b/features/pixels.js
index d471e620af382abdda1e7f3a7b092472127e88ff..973c66359093eee0cec218c8c761e634fb91c15e 100644
--- a/features/pixels.js
+++ b/features/pixels.js
@@ -25,7 +25,7 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) {
   var mean=[];
   mean[0]=0; mean[1]=0; mean[2]=0;
   var pos=0; var count=0;
-  for (var y=y0;y<y0+dy;y++)
+  for (var y=y0;y<y0+dy;y++) {
     for (var x=x0;x<x0+dx;x++) {
       pos=(y*imageData.width+x)<<2;
       if (imageData.data[pos+3]>0) {
@@ -35,11 +35,15 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) {
         count++;
       }
     }
+  }
   if (count>0) {
+    console.log("count: "+count);
     for (var i=0;i<3;i++) {
       mean[i]=Math.round(mean[i]/count);
     }
     return mean;
+  } else {
+    console.log("no positive Alpha, no rgb mean");
   }
   return undefined;
 }
@@ -53,3 +57,38 @@ pixels_features.mean_rgb_per_region=function(imageData,opt_options) {
 pixels_features.grid_mean_rgb=function(imageData,opt_options) {
   return generic_features.grid_descriptor(imageData,pixels_features.mean_rgb_per_region,opt_options);
 }
+
+/*
+  pixels_features.mean_rgb_afactor_per_region
+  - computes RGB mean of all pixels considering A as a weight of RGB channels
+  within opt_options.x0 .y0 .dx .dy
+  - if opt_options missing partially,
+      replace partially with defaults 0, 0, imageData.width, imageData.height
+  - returns undefined if none available
+*/
+pixels_features.mean_rgb_afactor_per_region=function(imageData,opt_options) {
+  x0=opt_options&&opt_options.x0?opt_options.x0:0;
+  y0=opt_options&&opt_options.y0?opt_options.y0:0;
+  dx=opt_options&&opt_options.dx?opt_options.dx:imageData.width;
+  dy=opt_options&&opt_options.dy?opt_options.dy:imageData.height;
+
+
+  var mean=[];
+  mean[0]=0; mean[1]=0; mean[2]=0;
+  var pos=0; var count=0;
+  for (var y=y0;y<y0+dy;y++)
+    for (var x=x0;x<x0+dx;x++) {
+      pos=(y*imageData.width+x)<<2;
+      for (var i=0;i<3;i++) {
+        mean[i]+=(imageData.data[pos+i]*imageData.data[pos+3]);
+      }
+      count++;
+    }
+  if (count>0) {
+    for (var i=0;i<3;i++) {
+      mean[i]=Math.round(mean[i]/count);
+    }
+    return mean;
+  }
+  return undefined;
+}
diff --git a/filters/linear.js b/filters/linear.js
new file mode 100644
index 0000000000000000000000000000000000000000..7747d6d90c291eba8c1904ea20e03dc1405b37d6
--- /dev/null
+++ b/filters/linear.js
@@ -0,0 +1,6 @@
+var linear_filters={}
+
+linear_filters.identity=function(imgData,x0,y0,region_width,region_height) {
+  var w=((y0+Math.round(region_height/2))*imgData.width+(x0+Math.round(region_width/2)))<<2;
+  return [imgData.data[w],imgData.data[w+1],imgData.data[w+2],imgData.data[w+3]];
+}
diff --git a/filters/morpho.js b/filters/morpho.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b6220586367833ec7dd6752b6ebb24f7feac84e
--- /dev/null
+++ b/filters/morpho.js
@@ -0,0 +1,84 @@
+var morpho_filters={}
+
+morpho_filters.max_from_region=function(imgData,x0,y0,reg_width,reg_height) {
+  var pixels=imgData.data;
+  var w=((y0+Math.round(reg_height/2))*imgData.width
+                                        +(x0+Math.round(reg_width/2)))<<2;
+  var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]];
+  var max=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+
+  for (var y=Math.max(0,y0);y<Math.min(y0+reg_height,imgData.height);y++)
+    for (var x=Math.max(0,x0);x<Math.min(x0+reg_width,imgData.width);x++) {
+      w = (y*imgData.width+x)<<2;
+      var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+      if (max < val) {
+        if (pixels[w+3]<255) pixels[w+3]=255;
+        max = val; max_data = [pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]];
+      }
+    }
+  return max_data;
+}
+
+morpho_filters.max_red_from_region=function(imgData,x0,y0,reg_width,reg_height) {
+  var pixels=imgData.data;
+  var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2;
+  var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]];
+  var max=(pixels[w]-pixels[w+1]-pixels[w+2])*pixels[w+3];
+  for (var y=y0;y<y0+reg_height;y++) {
+    if (y<0 || y>imgData.height) continue;
+    for (var x=x0;x<x0+reg_width;x++) {
+      if (x<0 || x>imgData.width) continue;
+      w = (y*imgData.width+x)<<2;
+      var val = (pixels[w]-pixels[w+1]-pixels[w+2])*pixels[w+3];
+      if (max < val) {
+        max = val;
+        max_data=[pixels[w], pixels[w+1],
+                  pixels[w+2], pixels[w+3]];
+      }
+    }
+  }
+  return max_data;
+}
+
+morpho_filters.max_gray_from_region=function(imgData,x0,y0,reg_width,reg_height) {
+  var pixels=imgData.data;
+  var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2;
+  var max_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]];
+  var max=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+
+  for (var y=y0;y<y0+reg_height;y++) {
+    if (y<0 || y>imgData.height) continue;
+    for (var x=x0;x<x0+reg_width;x++) {
+      if (x<0 || x>imgData.width) continue;
+      w = (y*imgData.width+x)<<2;
+      var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+      if (max < val) {
+        max = val;
+        max_data=[val/3, val/3,
+                  val/3, pixels[w+3]];
+      }
+    }
+  }
+  return max_data;
+}
+
+morpho_filters.min_from_region=function(imgData,x0,y0,reg_width,reg_height) {
+  var pixels=imgData.data;
+  var w=((y0+Math.round(reg_height/2))*imgData.width+(x0+Math.round(reg_width/2)))<<2;
+  var min_data=[pixels[w],pixels[w+1],pixels[w+2],pixels[w+3]];
+  var min=(pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+  for (var y=y0;y<y0+reg_height;y++) {
+    if (y<0 || y>imgData.height) continue;
+    for (var x=x0;x<x0+reg_width;x++) {
+      if (x<0 || x>imgData.width) continue;
+      w = (y*imgData.width+x)<<2;
+      var val = (pixels[w]+pixels[w+1]+pixels[w+2])*pixels[w+3];
+      if (min > val) {
+        min = val;
+        min_data=[pixels[w], pixels[w+1],
+                  pixels[w+2], pixels[w+3]];
+      }
+    }
+  }
+  return min_data;
+}
diff --git a/metrics/pixels.js b/metrics/pixels.js
index 2a9441cc8e86d627da0cd0027b523e51480a7609..954dd6c269d4018963d36e9e447e39321cc4c3c1 100644
--- a/metrics/pixels.js
+++ b/metrics/pixels.js
@@ -9,10 +9,42 @@ pixel_metrics.rgb_edist=function(pixel_rgb1, pixel_rgb2) {
 }
 
 /*
-    pixel_metrics.rgb_edist - computes euclidian distance between two grids
+    pixel_metrics.grid_rgb_edist - computes euclidian distance between two grids
     containing in each cell an rgb pixel
 */
 pixel_metrics.grid_rgb_edist=function(pixels_rgb_grid1, pixels_rgb_grid2) {
   var dist_fun=pixel_metrics.rgb_edist;
   return generic_metrics.euclidian_distance_btw_feature_vectors(pixels_rgb_grid1.cells,pixels_rgb_grid2.cells,dist_fun);
 }
+
+/*
+    pixel_metrics.pixel_edist - computes euclidian distance between two pixels
+    regardless of color encoding
+*/
+pixel_metrics.pixel_edist=function(pixel_rgb1, pixel_rgb2) {
+  var dist_fun=function(x,y){return x-y};
+  return generic_metrics.euclidian_distance_btw_feature_vectors(pixel_rgb1,pixel_rgb2,dist_fun);
+}
+
+/*
+    pixel_metrics.gray_edist - computes euclidian distance between two pixels
+    considering the intensity value
+*/
+pixel_metrics.gray_edist=function(pixel_rgb1, pixel_rgb2) {
+  var pixel_gray_1=Math.round((pixel_rgb1[0]+pixel_rgb1[1]+pixel_rgb1[2])/3);
+  var pixel_gray_2=Math.round((pixel_rgb2[0]+pixel_rgb2[1]+pixel_rgb2[2])/3);
+
+  return Math.abs(pixel_gray_1-pixel_gray_2);
+}
+
+/*
+    pixel_metrics.visible_edist - computes euclidian distance between two pixels
+    considering the fact that pixels are both visible/invisible
+    returns 0 or 1
+*/
+pixel_metrics.visible_edist=function(pixel_rgb1, pixel_rgb2) {
+  var pixel_gray_1=Math.round((pixel_rgb1[0]+pixel_rgb1[1]+pixel_rgb1[2])/3);
+  var pixel_gray_2=Math.round((pixel_rgb2[0]+pixel_rgb2[1]+pixel_rgb2[2])/3);
+  return (pixel_gray_1>0 && pixel_gray_2>0) || (pixel_gray_1==0 && pixel_gray_2==0);
+
+}
diff --git a/movement/detection.js b/movement/detection.js
new file mode 100644
index 0000000000000000000000000000000000000000..d264ba932b800bc9d0797461bdfaeed446a770d9
--- /dev/null
+++ b/movement/detection.js
@@ -0,0 +1,55 @@
+var mvt_detection={}
+
+mvt_detection.SimpleMvtDetectorRegion=function(opt_options) {
+  this.difference = document.createElement("canvas");
+  this.previous = document.createElement("canvas");
+
+  this.bbox=opt_options.bbox;
+
+  this.difference.width=this.bbox.dx; this.difference.height=this.bbox.dy;
+  this.previous.width=this.bbox.dx; this.previous.height=this.bbox.dy;
+
+  this.difference_ctxt = this.difference.getContext("2d");
+  this.previous_ctxt = this.previous.getContext("2d");
+
+  this.diff_threshold=opt_options.diff_threshold;
+  this.mvt_threshold=opt_options.mvt_threshold;
+}
+
+mvt_detection.SimpleMvtDetectorRegion.prototype.set_first_frame_from_eltId=function(eltId) {
+  var elt=document.getElementById(eltId);
+  var cvs=document.createElement("canvas");
+  cvs.width=elt.width; cvs.height=elt.height;
+  var ctxt=cvs.getContext("2d");
+  ctxt.drawImage(elt,0,0);
+  var first_imgData=ctxt.getImageData(this.bbox.x0,this.bbox.y0,this.bbox.dx,this.bbox.dy);
+  this.previous_ctxt.putImageData(first_imgData,0,0);
+}
+
+mvt_detection.SimpleMvtDetectorRegion.prototype.process=function(in_imgData, out_imgData) {
+  var previous_imgData = this.previous_ctxt.
+                            getImageData(0, 0, this.bbox.dx, this.bbox.dy);
+  in_reg_imgData=in_imgData.ctxt.getImageData(this.bbox.x0,this.bbox.y0,this.bbox.dx,this.bbox.dy);
+
+  var w=0;
+  var count=0;
+  var min=[255,255,255], max=[0,0,0];
+  for (var x=0; x<this.bbox.dx ; x++)
+    for (var y=0; y<this.bbox.dy ; y++) {
+      var diff=0;
+        for (var i=0; i<3; i++) {
+          out_imgData.data[w+i] = Math.abs(in_reg_imgData.data[w+i] - previous_imgData.data[w+i]);
+          diff += out_imgData.data[w+i];
+        }
+        count+=(diff>this.diff_threshold);
+        out_imgData.data[w+3] = 255;
+        w+=4;
+    }
+
+  this.previous_ctxt.putImageData(in_reg_imgData,0,0);
+  if (out_imgData) {
+    Tools.copy_partial_imageData_into_imageData(in_imgData,0,0,in_imgData.width,in_imgData.height,out_imgData,0,0);
+    Tools.strokeBBox_on_imageData(out_imgData,this.bbox,[255*(count>this.mvt_threshold),0,0,255]);
+  }
+  return (count>this.mvt_threshold);
+}
diff --git a/movement/difference.js b/movement/difference.js
new file mode 100644
index 0000000000000000000000000000000000000000..466416996163f9bf4a26a00eaae8b554a0ea1891
--- /dev/null
+++ b/movement/difference.js
@@ -0,0 +1,77 @@
+var diff={}
+
+diff.DifferenceImageRGB=function(opt_options) {
+  this.difference = document.createElement("canvas");
+  this.previous = document.createElement("canvas");
+
+  this.width=opt_options.width;
+  this.height=opt_options.height;
+
+  this.difference.width=this.width; this.difference.height=this.height;
+  this.previous.width=this.width; this.previous.height=this.height;
+
+  this.difference_ctxt = this.difference.getContext("2d");
+  this.previous_ctxt = this.previous.getContext("2d");
+}
+
+diff.DifferenceImageRGB.prototype.set_first_frame_imgData=function(imgData) {
+  this.previous_ctxt.putImageData(imgData,0,0);
+}
+
+diff.DifferenceImageRGB.prototype.set_first_frame_from_eltId=function(eltId) {
+  var elt=document.getElementById(eltId);
+  var cvs=document.createElement("canvas");
+  cvs.width=elt.width; cvs.height=elt.height;
+  var ctxt=cvs.getContext("2d");
+  ctxt.drawImage(elt,0,0);
+  this.set_first_frame_imgData(ctxt.getImageData(0,0,cvs.width,cvs.height));
+}
+
+diff.DifferenceImageRGB.prototype.process=function(in_imgData, out_imgData) {
+  var previous_imgData = this.previous_ctxt.
+                            getImageData(0, 0, this.width, this.height);
+  var w=0;
+  var min=[255,255,255], max=[0,0,0];
+  for (var x=0; x<this.width ; x++)
+    for (var y=0; y<this.height ; y++) {
+        for (var i=0; i<3; i++) {
+          out_imgData.data[w+i] =
+          Math.abs(in_imgData.data[w+i] - previous_imgData.data[w+i]);
+        }
+        out_imgData.data[w+3] = 255;
+        w+=4;
+    }
+  this.previous_ctxt.putImageData(in_imgData,0,0);
+}
+
+diff.NormalizedDifferenceImageRGB=function(opt_options) {
+  this.__proto__.__proto__=new diff.DifferenceImageRGB(opt_options);
+}
+
+diff.NormalizedDifferenceImageRGB.prototype.process=function(in_imgData, out_imgData) {
+  var previous_imgData = this.previous_ctxt.
+                            getImageData(0, 0, this.width, this.height);
+  var w=0;
+  var min=[255,255,255], max=[0,0,0];
+  for (var x=0; x<this.width ; x++)
+    for (var y=0; y<this.height ; y++) {
+        for (var i=0; i<3; i++) {
+          out_imgData.data[w+i] = Math.abs(in_imgData.data[w+i] - previous_imgData.data[w+i]);
+          if (min[i]>out_imgData.data[w+i]) min[i]=out_imgData.data[w+i];
+          if (max[i]<out_imgData.data[w+i]) max[i]=out_imgData.data[w+i];
+        }
+        out_imgData.data[w+3] = 255;
+        w+=4;
+    }
+
+  w=0;
+  for (var x=0; x<this.width ; x++)
+    for (var y=0; y<this.height ; y++) {
+        for (var i=0; i<3; i++) {
+          out_imgData.data[w+i] = (out_imgData.data[w+i]-min[i])*255/(max[i]-min[i]);
+        }
+        out_imgData.data[w+3] = 255;
+        w+=4;
+    }
+  this.previous_ctxt.putImageData(in_imgData,0,0);
+}
diff --git a/processing.js b/processing.js
index 0f262d9a44da8f77bfc0f710fde4e0fa518e1466..675e365e73a75b2e2a61ff400d65c6368505fa19 100644
--- a/processing.js
+++ b/processing.js
@@ -5,11 +5,14 @@ var processing=function(elementId,task,outputCanvasId) {
 
   this.imageData = {};
 
-  this.processing_canvas=document.createElement('canvas');
-  this.processing_canvas.width = this.width;
-  this.processing_canvas.height = this.height;
-
-  this.processing_context=this.processing_canvas.getContext("2d");
+  if (this.element.nodeName.toLowerCase()!="canvas") {
+    this.processing_canvas=document.createElement('canvas');
+    this.processing_canvas.width = this.width;
+    this.processing_canvas.height = this.height;
+    this.processing_context=this.processing_canvas.getContext("2d");
+  } else {
+    this.processing_canvas=this.element;
+  }
 
   this.task=task;
 
@@ -30,8 +33,7 @@ processing.prototype.acquire_data_from_video=function() {
 }
 
 processing.prototype.acquire_data_from_canvas=function() {
-  this.processing_context.drawImage(this.element,0,0,this.width,this.height);
-  this.imageData = this.processing_context.getImageData(0, 0, this.width, this.height);
+  this.imageData = this.this.processing_canvas.getContext("2d").getImageData(0, 0, this.width, this.height);
 }
 
 processing.prototype.acquire_data=function() {
diff --git a/processing2.js b/processing2.js
index eba599fefdcc55278cb3fcaa1b54f5d1fc3c06dc..0daf79213dbe249e9f13080d3218e933cbf967e7 100644
--- a/processing2.js
+++ b/processing2.js
@@ -8,12 +8,15 @@ var processing2=function(elementId,task,outputCanvasId,opt_options) {
   this.in_imageData = {};
   this.out_imageData = {};
 
-  this.processing_canvas=document.createElement('canvas');
-  this.processing_canvas.width = this.width;
-  this.processing_canvas.height = this.height;
+  if (this.element.nodeName.toLowerCase()!="canvas") {
+    this.processing_canvas=document.createElement('canvas');
+    this.processing_canvas.width = this.width;
+    this.processing_canvas.height = this.height;
+  } else {
+    this.processing_canvas=this.element;
+  }
 
   this.processing_context=this.processing_canvas.getContext("2d");
-
   this.task=task;
 
 
@@ -46,13 +49,14 @@ processing2.prototype.acquire_data_from_video=function() {
 }
 
 processing2.prototype.acquire_data_from_canvas=function() {
-  this.processing_context.drawImage(this.element,0,0,this.width,this.height);
   this.in_imageData = this.processing_context.getImageData(this.in_region.x, this.in_region.y, this.in_region.width, this.in_region.height);
 }
 
 processing2.prototype.acquire_data=function() {
-  if (this.output_canvas)
+  if (this.output_canvas) {
     this.out_imageData=this.output_context.getImageData(this.out_region.x,this.out_region.y,this.out_region.width,this.out_region.height);
+    this.out_imageData.ctxt=this.output_context;
+  }
 
   switch (this.element.nodeName.toLowerCase()) {
       case 'canvas':
@@ -64,6 +68,7 @@ processing2.prototype.acquire_data=function() {
       default:
         throw new Error('Element not supported!');
     }
+    this.in_imageData.ctxt = this.processing_context;
 }
 
 processing2.prototype.do_process=function() {
diff --git a/samples/blobs_appearance.html b/samples/blobs_appearance.html
new file mode 100644
index 0000000000000000000000000000000000000000..5db9f99fa188095ecc9f87a88b19d1c61c54aea8
--- /dev/null
+++ b/samples/blobs_appearance.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../segmentation/blobs.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/rgb.png" width="50" height="50"></img>
+  <canvas id="output" width="50" height="50"></canvas>
+
+
+
+  <script lang="javascript">
+    var _opt_options={metric:pixel_metrics.gray_edist,threshold:10};
+    var _task1=new blobs.Pixel8ConnectivityBlobs(_opt_options);
+    var _proc1=new processing2("input",_task1,"output");
+    _proc1.do_process();
+    var components_bbox=_proc1.get_result();
+
+    /*
+    var cvs_ctxt=document.getElementById("output").getContext("2d");
+    console.log(components_bbox);
+    for (idx in components_bbox) {
+      cvs_ctxt.strokeStyle=[components_bbox[idx].mean[0],
+      components_bbox[idx].mean[1],components_bbox[idx].mean[2],255];
+      cvs_ctxt.strokeRect(
+          components_bbox[idx].x, components_bbox[idx].y,
+          components_bbox[idx].width, components_bbox[idx].height,
+          );
+    }*/
+  </script>
+</body>
+</html>
diff --git a/samples/blobs_diff_img_dilatation_erosion.html b/samples/blobs_diff_img_dilatation_erosion.html
new file mode 100644
index 0000000000000000000000000000000000000000..bf7ae5698f6810996772c40aa4cd5872e8bf60c7
--- /dev/null
+++ b/samples/blobs_diff_img_dilatation_erosion.html
@@ -0,0 +1,71 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../effects/generic.js"></script>
+<script lang="js" src="../effects/morpho.js"></script>
+<script lang="js" src="../filters/morpho.js"></script>
+<script lang="js" src="../filters/linear.js"></script>
+
+<script lang="js" src="../segmentation/blobs.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/16_03.jpg" width="320" height="180"></img>
+  <canvas id="output1" width="320" height="180"></canvas><br></br>
+  <canvas id="output2" width="320" height="180" style="display:none"></canvas>
+  <canvas id="output3" width="320" height="180"></canvas>
+  <canvas id="output4" width="320" height="180"></canvas>
+
+
+  <script lang="javascript">
+    var _focus_options={focus_x:50,focus_y:50,focus_dx:5,focus_dy:5,focus_radius:50,width:320,height:180};
+    var _task1=new focus.MovingFocus(_focus_options);
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var _opt_options={width:320,height:180};
+    var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+
+    var _opt_options={dilatation_width:8,dilatation_height:8,
+                      erosion_width:2,erosion_height:2};
+    var _task3=new morpho_effects.DilatationErosionWindow(_opt_options);
+    var _proc3=new processing2("output2",_task3,"output3");
+
+    var _opt_options={metric:pixel_metrics.visible_edist,threshold:0};
+    var _task4=new blobs.Pixel8ConnectivityBlobs(_opt_options);
+    var _proc4=new processing2("output3",_task4,"output4");
+
+    var img_elt=document.getElementById("input");
+    img_elt.addEventListener("loadeddata",function(){
+      _task2.set_first_frame_from_eltId("input");
+    });
+    var count=0;
+    var cvs4=document.getElementById("output4");
+    var cvs4_ctxt=cvs4.getContext("2d");
+    var loop=function() {
+      _proc1.do_process();
+      _task1.updateFocus();
+
+      _proc2.do_process();
+      _proc3.do_process();
+
+      cvs4_ctxt.beginPath();
+      cvs4_ctxt.clearRect(0,0,cvs4.width,cvs4.height);
+      _proc4.do_process();
+
+      count++;
+
+      if (count<1000)
+        setTimeout(loop,200);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/blobs_diff_img_no_normalization.html b/samples/blobs_diff_img_no_normalization.html
new file mode 100644
index 0000000000000000000000000000000000000000..d39b7bde53674d460ea59a0421ca1a4c4777a316
--- /dev/null
+++ b/samples/blobs_diff_img_no_normalization.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../movement/detection.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../segmentation/blobs.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/16_03.jpg" width="320" height="180"></img>
+  <canvas id="output1" width="320" height="180"></canvas>
+  <canvas id="output2" width="320" height="180"></canvas>
+  <canvas id="output3" width="320" height="180"></canvas>
+
+
+  <script lang="javascript">
+    var _focus_options={focus_x:20,focus_y:20,focus_radius:20,width:320,height:180};
+    var _task1=new focus.MovingFocus(_focus_options);
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var _opt_options={width:320,height:180};
+    var _task2=new diff.DifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+
+    var _opt_options={metric:pixel_metrics.visible_edist,threshold:0};
+    var _task3=new blobs.Pixel8ConnectivityBlobs(_opt_options);
+    var _proc3=new processing2("output2",_task3,"output3");
+
+    var img_elt=document.getElementById("input");
+    img_elt.addEventListener("loadeddata",function(){
+      _task2.set_first_frame_from_eltId("input");
+    });
+
+    var cvs3=document.getElementById("output3");
+    var cvs3_ctxt=cvs3.getContext("2d");
+
+    var count=0;
+    var loop=function() {
+      _proc1.do_process();
+      _task1.updateFocus();
+
+      _proc2.do_process();
+
+      cvs3_ctxt.beginPath();
+      cvs3_ctxt.clearRect(0,0,cvs3.width,cvs3.height);
+      _proc3.do_process();
+      count++;
+
+      if (count<100)
+        setTimeout(loop,100);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/blobs_diff_img_with_normalization.html b/samples/blobs_diff_img_with_normalization.html
new file mode 100644
index 0000000000000000000000000000000000000000..8fd06614323158694e93120a64a448c6f4fcf05b
--- /dev/null
+++ b/samples/blobs_diff_img_with_normalization.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../segmentation/blobs.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/16_03.jpg" width="320" height="180"></img>
+  <canvas id="output1" width="320" height="180"></canvas><br></br>
+  <canvas id="output2" width="320" height="180"></canvas>
+  <canvas id="output3" width="320" height="180"></canvas>
+
+
+
+  <script lang="javascript">
+    var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180};
+    var _task1=new focus.MovingFocus(_focus_options);
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var _opt_options={width:320,height:180};
+    var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+
+    var _opt_options={metric:pixel_metrics.visible_edist,threshold:0};
+    var _task3=new blobs.Pixel8ConnectivityBlobs(_opt_options);
+    var _proc3=new processing2("output2",_task3,"output3");
+
+    var img_elt=document.getElementById("input");
+    img_elt.addEventListener("loadeddata",function(){
+      _task2.set_first_frame_from_eltId("input");
+    });
+
+    var cvs3=document.getElementById("output3");
+    var cvs3_ctxt=cvs3.getContext("2d");
+
+    var count=0;
+    var loop=function() {
+      _proc1.do_process();
+      _task1.updateFocus();
+
+      _proc2.do_process();
+      
+      cvs3_ctxt.beginPath();
+      cvs3_ctxt.clearRect(0,0,cvs3.width,cvs3.height);
+      _proc3.do_process();
+      count++;
+      console.log(count);
+      if (count<100)
+        setTimeout(loop,200);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/difference_animation.html b/samples/difference_animation.html
new file mode 100644
index 0000000000000000000000000000000000000000..c42fcd155435905a40e0264ca58d2c3b2af56f5f
--- /dev/null
+++ b/samples/difference_animation.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../features/generic.js"></script>
+<script lang="js" src="../features/pixels.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../animation/moving_circle.js"></script>
+<script lang="js" src="../tracking/appearance.js"></script>
+</head>
+<body><!--style="background-color:black; vertical-align: top;"-->
+
+  <canvas id="input" width="300" height="200"  style="vertical-align: top;"></canvas>
+  <canvas id="output" width="300" height="200"  style="vertical-align: top;"></canvas>
+
+
+  <script lang="javascript">
+    var _opt_options={
+      x0:40,y0:40,radius:40,step_x:10,step_y:10,random:true,
+    };
+    var _task1=new animations.MovingCircle("input",_opt_options);
+
+    var _opt_options={width:300,height:200,threshold:1};
+    var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("input",_task2,"output");
+
+    _task1.draw();
+    _task2.set_first_frame_from_eltId("input");
+
+    var loop=function() {
+      _task1.animate();
+      _proc2.do_process();
+
+      setTimeout(loop,200);
+    }
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/difference_effect.html b/samples/difference_effect.html
new file mode 100644
index 0000000000000000000000000000000000000000..23e89256b20a0ac3d7a6d2ce64c9cf4285de88cc
--- /dev/null
+++ b/samples/difference_effect.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/16_05.jpg" width="320" height="180"></img>
+  <canvas id="output1" width="320" height="180"></canvas><br/>
+  <canvas id="output2" width="320" height="180"></canvas>
+  <canvas id="output3" width="320" height="180"></canvas>
+
+
+  <script lang="javascript">
+    var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180,random:true,
+    focus_dx:20,focus_dy:20};
+    var _task1=new focus.MovingFocus(_focus_options);
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var _opt_options={width:320,height:180,threshold:50};
+    var _task2=new diff.DifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+
+    var _opt_options={width:320,height:180,threshold:1};
+    var _task3=new diff.NormalizedDifferenceImageRGB(_opt_options);
+    var _proc3=new processing2("output1",_task3,"output3");
+
+    var img_elt=document.getElementById("input");
+    img_elt.addEventListener("loadeddata",function(){
+      _task2.set_first_frame_from_eltId("input");
+    });
+    var loop=function() {
+      _proc1.do_process();
+      _task1.updateFocus();
+
+      _proc2.do_process();
+      _proc3.do_process();
+
+      setTimeout(loop,200);
+    }
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/difference_video.html b/samples/difference_video.html
new file mode 100644
index 0000000000000000000000000000000000000000..bbdbe06318c9a9b6a9bb4c5e3e60d009644eb9dd
--- /dev/null
+++ b/samples/difference_video.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+
+</head>
+<body>
+  <video autoplay loop src="../data/surprise.mp4" id="input" width="320" height="180"></video>
+  <canvas id="output1" width="320" height="180"></canvas>
+
+  <script lang="javascript">
+    var _opt_options={width:320,height:180};
+
+    var _task1=new diff.DifferenceImageRGB(_opt_options);
+    var video_elt=document.getElementById("input");
+    video_elt.addEventListener("loadeddata",function(){
+      _task1.set_first_frame_from_eltId("input");
+    });
+
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var loop=function() {
+      _proc1.do_process();
+      requestAnimationFrame(loop);
+    }
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/meanshift_changing_circle_and_bg_color.html b/samples/meanshift_changing_circle_and_bg_color.html
new file mode 100644
index 0000000000000000000000000000000000000000..cf37095199a78e480c2ca434b32a89a6b0cfc7e5
--- /dev/null
+++ b/samples/meanshift_changing_circle_and_bg_color.html
@@ -0,0 +1,66 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../features/generic.js"></script>
+<script lang="js" src="../features/pixels.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../animation/moving_circle.js"></script>
+<script lang="js" src="../tracking/appearance.js"></script>
+</head>
+<body><!-- style="background-color:black;"-->
+
+  <canvas id="input" width="150" height="100" style="vertical-align:top" ></canvas>
+  <canvas id="output2" width="250" height="400"></canvas>
+
+
+  <script lang="javascript">
+    var _opt_options={
+      x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true,
+      fillColor0:[255,0,0,255],
+      fillColor1:[127,0,0,255],
+      fillColorNbSteps:100,
+      bgColor0:[127,0,0,255],
+      bgColor1:[255,0,0,255],
+      bgColorNbSteps:100
+    };
+    var _task1=new animations.MovingCircleHChangingColorChangingBackground("input",_opt_options);
+
+    var _bbox={x0:0,y0:0,dx:20,dy:20};
+
+    var _tracking_options={
+      window_width:40,
+      window_height:40,
+      step_x:3,
+      step_y:3,
+      update_model:false
+    };
+
+    var _task2=new appearance_tracking.MeanShift(_bbox,
+      pixels_features.mean_rgb_afactor_per_region,
+      pixel_metrics.pixel_edist,
+      _tracking_options);
+
+    var _proc2=new processing2("input",_task2,"output2");
+
+
+    var count=0;
+    _task1.draw();
+    var loop=function() {
+      _proc2.do_process();
+      _task1.animate();
+
+      count++;
+
+      if (count<1000)
+        setTimeout(loop,100);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/meanshift_changing_circle_color.html b/samples/meanshift_changing_circle_color.html
new file mode 100644
index 0000000000000000000000000000000000000000..3b8ebfb8b8dc24ee11c8e967a00fb5a092e0af4d
--- /dev/null
+++ b/samples/meanshift_changing_circle_color.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../features/generic.js"></script>
+<script lang="js" src="../features/pixels.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../animation/moving_circle.js"></script>
+<script lang="js" src="../tracking/appearance.js"></script>
+</head>
+<body><!--style="background-color:black; vertical-align: top;"-->
+
+  <canvas id="input" width="150" height="100"  style="vertical-align: top;"></canvas>
+  <canvas id="output2" width="250" height="700" style="vertical-align: top;"></canvas>
+
+
+  <script lang="javascript">
+    var _opt_options={
+      x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true,
+      fillColor0:[255,0,0,255],
+      fillColor1:[0,255,0,255],
+      fillColorNbSteps:100
+    };
+    var _task1=new animations.MovingCircleHChangingColor("input",_opt_options);
+
+    var _bbox={x0:0,y0:0,dx:20,dy:20};
+
+    var _tracking_options={
+      window_width:60,
+      window_height:60,
+      step_x:3,
+      step_y:3,
+      update_model:false
+    };
+
+    var _task2=new appearance_tracking.MeanShift(_bbox,
+      pixels_features.mean_rgb_afactor_per_region,
+      pixel_metrics.pixel_edist,_tracking_options);
+
+    var _proc2=new processing2("input",_task2,"output2");
+
+
+    var count=0;
+    _task1.draw();
+    var loop=function() {
+      _proc2.do_process();
+      _task1.animate();
+
+      count++;
+
+      if (count<1000)
+        setTimeout(loop,100);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/meanshift_circle.html b/samples/meanshift_circle.html
new file mode 100644
index 0000000000000000000000000000000000000000..8dbf138186d7941987615f62399cf665a86f6b5e
--- /dev/null
+++ b/samples/meanshift_circle.html
@@ -0,0 +1,56 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../features/generic.js"></script>
+<script lang="js" src="../features/pixels.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../animation/moving_circle.js"></script>
+<script lang="js" src="../tracking/appearance.js"></script>
+</head>
+<body> <!--style="background-color:black;" -->
+
+  <canvas id="input" width="150" height="100" style="vertical-align:top" ></canvas>
+  <canvas id="output2" width="250" height="700"></canvas>
+
+
+  <script lang="javascript">
+    var _opt_options={x0:10,y0:10,radius:10,step_x:2,step_y:2,random:true,fillStyle:"red"};
+    var _task1=new animations.MovingCircle("input",_opt_options);
+
+    var _bbox={x0:0,y0:0,dx:20,dy:20};
+
+    var _tracking_options={
+      window_width:40,
+      window_height:40,
+      step_x:3,
+      step_y:3
+    };
+
+    var _task2=new appearance_tracking.MeanShift(_bbox,
+      pixels_features.mean_rgb_afactor_per_region,
+      pixel_metrics.pixel_edist,_tracking_options);
+
+    var _proc2=new processing2("input",_task2,"output2");
+
+
+    var count=0;
+    _task1.draw();
+    var loop=function() {
+      _proc2.do_process();
+      _task1.animate();
+
+      count++;
+
+      if (count<1000)
+        setTimeout(loop,100);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/samples/morpho_dilatation.html b/samples/morpho_dilatation.html
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/samples/morpho_dilatation_erosion.html b/samples/morpho_dilatation_erosion.html
new file mode 100644
index 0000000000000000000000000000000000000000..663b1f65d2f20b0da7cb386c9930d9cd4454e575
--- /dev/null
+++ b/samples/morpho_dilatation_erosion.html
@@ -0,0 +1,49 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../filters/morpho.js"></script>
+<script lang="js" src="../effects/generic.js"></script>
+<script lang="js" src="../effects/morpho.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/red_circle.gif" width="320" height="200"></img>
+  <canvas id="output1"></canvas>
+  <canvas id="output2"></canvas>
+  <br></br>
+  <img id="input2" src="../data/16_03.jpg" width="320" height="200"></img>
+  <canvas id="output3"></canvas>
+  <canvas id="output4"></canvas>
+
+
+
+  <script lang="javascript">
+    Tools.copySize("input","output1");
+    Tools.copySize("input","output2");
+
+    var _opt_options={window_width:10,window_height:10};
+    var _task1=new morpho_effects.DilatationWindow(_opt_options);
+    var _proc1=new processing2("input",_task1,"output1");
+    _proc1.do_process();
+    var _task2=new morpho_effects.ErosionWindow(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+    _proc2.do_process();
+
+    Tools.copySize("input2","output3");
+    Tools.copySize("input2","output4");
+
+    var _opt_options={window_width:10,window_height:10};
+    var _task3=new morpho_effects.DilatationWindow(_opt_options);
+    var _proc3=new processing2("input2",_task3,"output3");
+    _proc3.do_process();
+    var _task4=new morpho_effects.ErosionWindow(_opt_options);
+    var _proc4=new processing2("output3",_task4,"output4");
+    _proc4.do_process();
+
+  </script>
+</body>
+</html>
diff --git a/samples/mvt_detection_region.html b/samples/mvt_detection_region.html
new file mode 100644
index 0000000000000000000000000000000000000000..37b331d61cebb17cbe985e382d3f70e20807b4b5
--- /dev/null
+++ b/samples/mvt_detection_region.html
@@ -0,0 +1,62 @@
+<html>
+<head>
+<script lang="js" src="../tools.js"></script>
+<script lang="js" src="../processing2.js"></script>
+<script lang="js" src="../movement/difference.js"></script>
+<script lang="js" src="../movement/detection.js"></script>
+<script lang="js" src="../effects/focus.js"></script>
+<script lang="js" src="../metrics/generic.js"></script>
+<script lang="js" src="../metrics/pixels.js"></script>
+<script lang="js" src="../segmentation/blobs.js"></script>
+</head>
+<body>
+
+  <img id="input" src="../data/16_03.jpg" width="320" height="180"></img>
+  <canvas id="output1" width="320" height="180"></canvas><br></br>
+  <canvas id="output2" width="320" height="180"></canvas>
+  <canvas id="output3" width="320" height="180"></canvas>
+
+
+
+  <script lang="javascript">
+    var _focus_options={focus_x:50,focus_y:50,focus_radius:50,width:320,height:180};
+    var _task1=new focus.MovingFocus(_focus_options);
+    var _proc1=new processing2("input",_task1,"output1");
+
+    var _opt_options={width:320,height:180};
+    var _task2=new diff.NormalizedDifferenceImageRGB(_opt_options);
+    var _proc2=new processing2("output1",_task2,"output2");
+
+    var _opt_options={bbox:{x0:100,y0:100,dx:50,dy:50},
+                      mvt_threshold:50,diff_threshold:30};
+    var _task3=new mvt_detection.SimpleMvtDetectorRegion(_opt_options);
+    var _proc3=new processing2("output1",_task3,"output3");
+
+    var img_elt=document.getElementById("input");
+    img_elt.addEventListener("loadeddata",function(){
+      _task2.set_first_frame_from_eltId("input");
+      _task5.set_first_frame_from_eltId("input");
+    });
+
+    var cvs3=document.getElementById("output3");
+    var cvs3_ctxt=cvs3.getContext("2d");
+
+    var count=0;
+    var loop=function() {
+      _proc1.do_process();
+      _task1.updateFocus();
+
+      _proc2.do_process();
+
+      _proc3.do_process();
+
+      count++;
+      if (count<100)
+        setTimeout(loop,100);
+    }
+
+    loop();
+
+  </script>
+</body>
+</html>
diff --git a/segmentation/blobs.js b/segmentation/blobs.js
new file mode 100644
index 0000000000000000000000000000000000000000..c2eee114de3a4515bd41d84783ce5bd9d934d99a
--- /dev/null
+++ b/segmentation/blobs.js
@@ -0,0 +1,93 @@
+var blobs={}
+
+blobs.Pixel8ConnectivityBlobs=function(opt_options) {
+  this.metric = opt_options.metric;
+  this.threshold=opt_options && opt_options.threshold ? opt_options.threshold : 0;
+}
+
+blobs.Pixel8ConnectivityBlobs.prototype.analyse_neighbours=function(in_imgData, x0, y0) {
+  w0=((y0*in_imgData.width)+x0)<<2;
+  var pixel0=[in_imgData.data[w0],in_imgData.data[w0+1],in_imgData.data[w0+2]];
+  var local_connectivity=[]
+  for (var dy=-1;dy<1;dy++) {
+    if (0>(dy+y0) || (dy+y0)>=in_imgData.height) continue;
+    for (var dx=-1;dx<(dy<0?2:1);dx++) {
+      if (0>(dx+x0) || (dx+x0)>=in_imgData.width) continue;
+      var w=((y0+dy)*in_imgData.width+x0+dx)<<2;
+      if (!this.component[w>>2]) continue;
+      var pixel=[in_imgData.data[w],in_imgData.data[w+1],in_imgData.data[w+2]]
+      if (this.metric(pixel0,pixel) <= this.threshold) {
+        var component=this.components[this.component[w>>2]];
+        if (local_connectivity.indexOf(component)==-1) {
+          local_connectivity.push(component);
+        }
+      }
+    }
+  }
+  local_connectivity.sort(function(a,b){return a<b?-1:((a==b)?0:1)});
+  return local_connectivity;
+}
+
+blobs.Pixel8ConnectivityBlobs.prototype.update_bbox=function(id,bbox,x,y,mean) {
+  if (bbox.x0>x) {bbox.dx+=bbox.x0-x; bbox.x0=x;}
+  if (bbox.y0>y) {bbox.dy+=bbox.y0-y; bbox.y0=y;}
+  if (bbox.x0+bbox.dx<x) bbox.dx=x-bbox.x0;
+  if (bbox.y0+bbox.dy<y) bbox.dy=y-bbox.y0;
+  bbox.count++;
+  for (i=0;i<3;i++)
+    bbox.mean[i]=((bbox.count-1)*bbox.mean[i]+mean[i])/bbox.count;
+  return bbox;
+}
+
+blobs.Pixel8ConnectivityBlobs.prototype.process=function(in_imgData,out_imgData) {
+  var blobs_arr=[]
+  this.component=[]; this.components=[];
+  this.nb_components=0;
+  var w=0;
+  for (var y=0; y < in_imgData.height; y++) {
+    for (var x=0; x < in_imgData.width; x++)
+    {
+      w=(y*in_imgData.width)+x;
+      var neighbours_connectivity=this.analyse_neighbours(in_imgData, x, y);
+      if (neighbours_connectivity.length==1) {
+        this.component[w]=(neighbours_connectivity[0]);
+      } else if (neighbours_connectivity.length>1) {
+        for (var i=1;i<neighbours_connectivity.length;i++) {
+          this.components[neighbours_connectivity[i]]=
+              this.components[neighbours_connectivity[0]];
+        }
+        this.component[w]=(this.components[neighbours_connectivity[0]]);
+      } else {
+          this.components[this.nb_components]=this.nb_components;
+          this.component[w]=(this.nb_components);
+          this.nb_components++;
+      }
+    }
+  }
+
+  var w=0, w4=0;
+  var components_bbox=[];
+  for (var y=0; y < in_imgData.height; y++)
+    for (var x=0; x < in_imgData.width; x++,w++,w4+=4)
+    {
+      this.component[w]=this.components[this.component[w]];
+      if (components_bbox[this.component[w]])
+        components_bbox[this.component[w]]=
+          this.update_bbox(
+            this.component[w],
+            components_bbox[this.component[w]],
+            x,y,
+            [in_imgData.data[w4],in_imgData.data[w4+1],in_imgData.data[w4+2]]);
+      else
+        components_bbox[this.component[w]]={
+                      x0:x,y0:y,dx:0,dy:0,
+                      count:1,mean:[in_imgData.data[w4],in_imgData.data[w4+1],
+                      in_imgData.data[w4+2]]};
+    }
+  if (out_imgData.data) {
+    for (idx in components_bbox) {
+    Tools.strokeBBox_on_imageData(out_imgData,components_bbox[idx],components_bbox[idx].mean);
+    }
+  }
+  return components_bbox;
+}
diff --git a/tools.js b/tools.js
index 40860ad494c3a8c5dc8e81e5e3fe97ed2b1ae75a..55320f514eaeb52acffb02558e913b2c5720bd6d 100644
--- a/tools.js
+++ b/tools.js
@@ -59,6 +59,21 @@ Tools.copy_imageData_into_imageData=function(src_imageData,dest_imageData,x0,y0)
     }
 }
 
+Tools.copy_partial_imageData_into_imageData=
+function(src_imageData,x0,y0,dx,dy,dest_imageData,x1,y1) {
+
+  for (var src_y=y0; src_y<Math.min(y0+dy,src_imageData.height); src_y++)
+    for (var src_x=x0; src_x < Math.min(x0+dx,src_imageData.width); src_x++) {
+      var w_src=(src_y*src_imageData.width+src_x)<<2;
+      w_out=((y1+(src_y-y0))*dest_imageData.width+(x1+(src_x-x0)))<<2;
+      dest_imageData.data[w_out]=src_imageData.data[w_src];
+      dest_imageData.data[w_out+1]=src_imageData.data[w_src+1];
+      dest_imageData.data[w_out+2]=src_imageData.data[w_src+2];
+      dest_imageData.data[w_out+3]=src_imageData.data[w_src+3];
+
+    }
+}
+
 Tools.get_region_from_imageData=function(src_imageData,x0,y0,dx,dy) {
   var reg_imageData=src_imageData.ctxt.getImageData(
       src_imageData.orig_x+x0,src_imageData.orig_y+y0,
@@ -70,3 +85,35 @@ Tools.get_region_from_imageData=function(src_imageData,x0,y0,dx,dy) {
 
   return reg_imageData;
 }
+
+Tools.strokeBBox_on_imageData=function(imgData,bbox,col) {
+  Tools.strokeRect_on_imageData(imgData,bbox.x0,bbox.y0,bbox.dx,bbox.dy,col);
+}
+
+Tools.strokeRect_on_imageData=function(imgData,x0,y0,width,height,col) {
+  var w;
+
+  for (var x=x0;x<x0+width;x++) {
+    w=((y0)*imgData.width+x)<<2;
+    imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2];
+    imgData.data[w+3]=255;
+    w=((y0+height)*imgData.width+x)<<2;
+    imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2];
+    imgData.data[w+3]=255;
+  }
+  for (var y=y0;y<y0+height;y++) {
+    w=(y*imgData.width+x0)<<2;
+    imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2];
+    imgData.data[w+3]=255;
+    w=(y*imgData.width+x0+width)<<2;
+    imgData.data[w]=col[0];imgData.data[w+1]=col[1];imgData.data[w+2]=col[2];
+    imgData.data[w+3]=255;
+  }
+}
+
+Tools.copySize=function(elt_id_1,elt_id_2) {
+  var elt1=document.getElementById(elt_id_1);
+  var elt2=document.getElementById(elt_id_2);
+  elt2.width=elt1.width;
+  elt2.height=elt1.height;
+}
diff --git a/tracking/appearance.js b/tracking/appearance.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a98aecddfaddb460b90692279d59f269c5de12f
--- /dev/null
+++ b/tracking/appearance.js
@@ -0,0 +1,87 @@
+var appearance_tracking={}
+
+appearance_tracking.MeanShift=function(bbox,per_region_feature_func,metric_func,opt_options){
+  this.bbox=bbox;
+  this.window_width=opt_options&&opt_options.window_width?opt_options.window_width:3*bbox.dx;
+  this.window_height=opt_options&&opt_options.window_height?opt_options.window_height:3*bbox.dy;
+  this.step_x=opt_options&&opt_options.step_x?opt_options.step_x:bbox.dx/3;
+  this.step_y=opt_options&&opt_options.step_y?opt_options.step_y:bbox.dy/3;
+  this.per_region_feature_func=per_region_feature_func;
+  this.metric_func=metric_func;
+  this.threshold=opt_options&&opt_options.threshold?opt_options.threshold:Number.MAX_SAFE_INTEGER;
+  this.count=0;
+  this.update_model=opt_options&&opt_options.update_model?opt_options.update_model:false;
+}
+
+appearance_tracking.MeanShift.prototype.process=function(in_imgData,out_imgData) {
+  var in_width=in_imgData.width, in_height=in_imgData.height;
+
+  if (!this.bbox_feature) {
+    var _opt_options=this.bbox;
+    this.bbox_feature=this.per_region_feature_func(in_imgData,_opt_options);
+  }
+
+  this.pan_x=Math.round(this.window_width/2-this.bbox.dx/2);
+  this.pan_y=Math.round(this.window_height/2-this.bbox.dy/2);
+
+  var x_start=(this.bbox.x0-this.pan_x)>0?(this.bbox.x0-this.pan_x):0;
+  var y_start=(this.bbox.y0-this.pan_y)>0?(this.bbox.y0-this.pan_y):0;
+  var x_end=(this.bbox.x0+this.bbox.dx+this.pan_x) < in_width?
+            (this.bbox.x0+this.pan_x)
+            :(in_width-this.bbox.dx);
+  var y_end=(this.bbox.y0+this.bbox.dy+this.pan_y)<in_height?
+            this.bbox.y0+this.pan_y
+            :(in_height-this.bbox.dy);
+
+  var min=Number.MAX_SAFE_INTEGER;
+  var min_bbox,min_bbox_feature;
+  var count_y=in_imgData.height;
+
+  if (out_imgData) {
+    Tools.copy_partial_imageData_into_imageData(in_imgData,
+                0,0,in_imgData.width,in_imgData.height,
+                out_imgData,0,0);
+    Tools.strokeBBox_on_imageData(out_imgData,{x0:x_start,y0:y_start,dx:x_end-x_start,dy:y_end-y_start},[64,64,64,255]);
+  }
+
+  for (var y=y_start;y<y_end;y+=this.step_y) {
+    var count_x=0;
+    for (var x=x_start;x<x_end;x+=this.step_x) {
+      var local_bbox={
+                    x0:(x+this.bbox.dx<in_width)?x:in_width-this.bbox.dx,
+                    y0:(y+this.bbox.dy<in_height)?y:in_height-this.bbox.dy,
+                    dx:this.bbox.dx,
+                    dy:this.bbox.dy
+                  }
+      if (out_imgData) {
+        Tools.copy_partial_imageData_into_imageData(in_imgData,
+                    local_bbox.x0,local_bbox.y0,local_bbox.dx,local_bbox.dy,
+                    out_imgData,count_x,count_y);
+        Tools.strokeBBox_on_imageData(out_imgData,local_bbox,[0,64,64,255]);
+                  }
+
+      var local_bbox_feature=this.per_region_feature_func(in_imgData,local_bbox);
+      if (!local_bbox_feature) continue;
+      var fun=function(a,b) {return a-b;}
+      var diff=this.metric_func(this.bbox_feature,local_bbox_feature);
+      if (diff < min) {
+        min=diff;
+        min_bbox=local_bbox;
+        min_bbox_feature=local_bbox_feature;
+      }
+      count_x+=this.bbox.dx;
+    }
+    count_y+=this.bbox.dy;
+  }
+
+  if (min < this.threshold && this.bbox!=min_bbox) {
+      this.count++;
+      this.bbox=min_bbox;
+      if (this.update_model)
+        this.bbox_feature=min_bbox_feature;
+  }
+  if (out_imgData) {
+    Tools.strokeBBox_on_imageData(out_imgData,this.bbox,[255,0,255,255]);
+  }
+  return this.bbox;
+}