diff --git a/src/pool2d.js b/src/pool2d.js
index 85fd174..1ee49e3 100644
--- a/src/pool2d.js
+++ b/src/pool2d.js
@@ -3,7 +3,7 @@
 import {computePaddingForAutoPad} from './lib/compute-padding.js';
 import {Tensor} from './lib/tensor.js';
 import {transpose} from './transpose.js';
-import {meanReducer, maxReducer} from './reduce.js';
+import {l2Reducer, meanReducer, maxReducer} from './reduce.js';
 import {validatePool2dParams} from './lib/validate-input.js';
 
 /**
@@ -26,7 +26,6 @@ function pool2d(input, reductionFunc,
     }= {}) {
   validatePool2dParams(...arguments);
   const roundingFunc = roundingType === 'floor' ? Math.floor : Math.ceil;
-
   if (layout === 'nhwc') {
     // nhwc -> nchw
     input = transpose(input, {permutation: [0, 3, 1, 2]});
@@ -125,3 +124,14 @@ export function averagePool2d(input, options = {}) {
 export function maxPool2d(input, options = {}) {
   return pool2d(input, maxReducer, options);
 }
+
+/**
+ * Compute a L2 reduction operation across all the elements within the moving window over
+ * the input tensor.
+ * @param {Tensor} input
+ * @param {MLPool2dOptions} options
+ * @return {Tensor}
+ */
+export function l2Pool2d(input, options = {}) {
+  return pool2d(input, l2Reducer, options);
+}
diff --git a/src/reduce.js b/src/reduce.js
index 390fa47..5806d35 100644
--- a/src/reduce.js
+++ b/src/reduce.js
@@ -143,6 +143,17 @@ export function reduceL1(input, options = {}) {
   return reduceSum(abs(input), options);
 }
 
+/* The l2 reducer */
+export function l2Reducer(previousValue, currentValue, currentIndex, array) {
+  if (currentIndex == 1) {
+    const sumOfSquares = previousValue * previousValue + currentValue * currentValue;
+    return sumOfSquares;
+  } else {
+    const sumOfSquares = previousValue + currentValue * currentValue;
+    return (currentIndex === array.length - 1) ? Math.sqrt(sumOfSquares) :sumOfSquares;
+  }
+}
+
 /**
  * Compute the L2 norm of all the input values along the axes.
  * @param {Tensor} input
diff --git a/test/l2pool2d_test.js b/test/l2pool2d_test.js
new file mode 100644
index 0000000..bb565d3
--- /dev/null
+++ b/test/l2pool2d_test.js
@@ -0,0 +1,285 @@
+'use strict';
+import {l2Pool2d} from '../src/pool2d.js';
+import {Tensor} from '../src/lib/tensor.js';
+
+import * as utils from './utils.js';
+
+describe('test pool2d', function() {
+  it('l2Pool2d default', function() {
+    const x = new Tensor([1, 1, 4, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+    const windowDimensions = [3, 3];
+    const y = l2Pool2d(x, {windowDimensions});
+    utils.checkShape(y, [1, 1, 2, 2]);
+    utils.checkValue(y, [
+      20.639767440550294,
+      23.302360395462088,
+      31.654383582688826,
+      34.51086785347479,
+    ]);
+  });
+
+  it('l2Pool2d default: a single element test case', function() {
+    const x = new Tensor([1, 1, 1, 1], [2]);
+    const windowDimensions = [1, 1];
+    const y = l2Pool2d(x, {windowDimensions});
+    utils.checkShape(y, [1, 1, 1, 1]);
+    utils.checkValue(y, [2]);
+  });
+
+  it('l2Pool2d default: start not from 1 ', function() {
+    const x = new Tensor([1, 1, 4, 4], [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]);
+    const windowDimensions = [3, 3];
+    const y = l2Pool2d(x, {windowDimensions});
+    utils.checkShape(y, [1, 1, 2, 2]);
+    utils.checkValue(y, [
+      34.51086785347479,
+      31.654383582688826,
+      23.302360395462088,
+      20.639767440550294,
+    ]);
+  });
+
+  it('l2Pool2d nhwc', function() {
+    const x = new Tensor([1, 4, 4, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+    const windowDimensions = [3, 3];
+    const layout = 'nhwc';
+    const y = l2Pool2d(x, {windowDimensions, layout});
+    utils.checkShape(y, [1, 2, 2, 1]);
+    utils.checkValue(y, [
+      20.639767440550294,
+      23.302360395462088,
+      31.654383582688826,
+      34.51086785347479,
+    ]);
+  });
+
+  it('l2Pool2d dilations default', function() {
+    const x = new Tensor([1, 1, 4, 4], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+    const windowDimensions = [2, 2];
+    const dilations = [2, 2];
+    const y = l2Pool2d(x, {windowDimensions, dilations});
+    utils.checkShape(y, [1, 1, 2, 2]);
+    utils.checkValue(y, [
+      14.560219778561036,
+      16.24807680927192,
+      21.633307652783937,
+      23.49468024894146,
+    ]);
+  });
+
+  it('l2Pool2d dilations nhwc', function() {
+    const x = new Tensor([1, 4, 4, 1], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+    const windowDimensions = [2, 2];
+    const dilations = [2, 2];
+    const layout = 'nhwc';
+    const y = l2Pool2d(x, {windowDimensions, dilations, layout});
+    utils.checkShape(y, [1, 2, 2, 1]);
+    utils.checkValue(y, [
+      14.560219778561036,
+      16.24807680927192,
+      21.633307652783937,
+      23.49468024894146,
+    ]);
+  });
+
+  it('l2Pool2d pads default', function() {
+    const x = new Tensor([1, 1, 5, 5], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [5, 5];
+    const padding = [2, 2, 2, 2];
+    const y = l2Pool2d(x, {windowDimensions, padding});
+    utils.checkShape(y, [1, 1, 5, 5]);
+    const expected = [
+      24.43358344574123, 29.832867780352597,
+      35.21363372331802,  32.89376840679705,
+      29.748949561287034,  38.28837943815329,
+      46.04345773288535,   53.5723809439155,
+      49.558046773455466, 44.384682042344295,
+      54.037024344425184,  64.42049363362563,
+      74.33034373659252,  68.33739825307956,
+      60.8276253029822, 53.907327887774215,
+      64.18722614352485,  73.95944834840239,
+      67.94115100585212,  60.41522986797286,
+      52.507142371300304, 62.369864518050704,
+      71.69379331573968,   65.7419196555744,
+      58.35237784358063,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d pads nhwc', function() {
+    const x = new Tensor([1, 5, 5, 1], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [5, 5];
+    const padding = [2, 2, 2, 2];
+    const layout = 'nhwc';
+    const y = l2Pool2d(x, {windowDimensions, padding, layout});
+    utils.checkShape(y, [1, 5, 5, 1]);
+    const expected = [
+      24.43358344574123, 29.832867780352597,
+      35.21363372331802,  32.89376840679705,
+      29.748949561287034,  38.28837943815329,
+      46.04345773288535,   53.5723809439155,
+      49.558046773455466, 44.384682042344295,
+      54.037024344425184,  64.42049363362563,
+      74.33034373659252,  68.33739825307956,
+      60.8276253029822, 53.907327887774215,
+      64.18722614352485,  73.95944834840239,
+      67.94115100585212,  60.41522986797286,
+      52.507142371300304, 62.369864518050704,
+      71.69379331573968,   65.7419196555744,
+      58.35237784358063,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d autoPad same-upper default', function() {
+    const x = new Tensor([1, 1, 5, 5], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [5, 5];
+    const autoPad = 'same-upper';
+    const y = l2Pool2d(x, {windowDimensions, autoPad});
+    utils.checkShape(y, [1, 1, 5, 5]);
+    const expected = [
+      24.43358344574123, 29.832867780352597,
+      35.21363372331802,  32.89376840679705,
+      29.748949561287034,  38.28837943815329,
+      46.04345773288535,   53.5723809439155,
+      49.558046773455466, 44.384682042344295,
+      54.037024344425184,  64.42049363362563,
+      74.33034373659252,  68.33739825307956,
+      60.8276253029822, 53.907327887774215,
+      64.18722614352485,  73.95944834840239,
+      67.94115100585212,  60.41522986797286,
+      52.507142371300304, 62.369864518050704,
+      71.69379331573968,   65.7419196555744,
+      58.35237784358063,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d autoPad explicit nhwc', function() {
+    const x = new Tensor([1, 7, 7, 1], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17,
+      18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+      35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+    ]);
+    const windowDimensions = [4, 4];
+    const padding = [2, 1, 2, 1];
+    const strides = [2, 2];
+    const autoPad = 'explicit';
+    const layout = 'nhwc';
+    const y = l2Pool2d(
+        x, {windowDimensions, autoPad, padding, strides, layout});
+    utils.checkShape(y, [1, 4, 4, 1]);
+    const expected = [
+      12.24744871391589,   19.8997487421324,
+      24.899799195977465, 24.879710609249457,
+      40.54626986542659, 60.860496218811754,
+      67.82329983125268, 63.324560795950255,
+      76.81145747868608,  112.5344391730816,
+      120.2331069215131,  109.1146186356347,
+      90.50414355155237,  131.4610208388783,
+      138.31124321616085, 124.21352583354198,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d autoPad same-lower nhwc', function() {
+    const x = new Tensor([1, 7, 7, 1], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17,
+      18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+      35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+    ]);
+    const windowDimensions = [4, 4];
+    const strides = [2, 2];
+    const autoPad = 'same-lower';
+    const layout = 'nhwc';
+    const y =
+        l2Pool2d(x, {windowDimensions, autoPad, strides, layout});
+    utils.checkShape(y, [1, 4, 4, 1]);
+    const expected = [
+      12.24744871391589,   19.8997487421324,
+      24.899799195977465, 24.879710609249457,
+      40.54626986542659, 60.860496218811754,
+      67.82329983125268, 63.324560795950255,
+      76.81145747868608,  112.5344391730816,
+      120.2331069215131,  109.1146186356347,
+      90.50414355155237,  131.4610208388783,
+      138.31124321616085, 124.21352583354198,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d autoPad same-upper nhwc', function() {
+    const x = new Tensor([1, 5, 5, 1], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [5, 5];
+    const autoPad = 'same-upper';
+    const layout = 'nhwc';
+    const y = l2Pool2d(x, {windowDimensions, autoPad, layout});
+
+    utils.checkShape(y, [1, 5, 5, 1]);
+    const expected = [
+      24.43358344574123, 29.832867780352597,
+      35.21363372331802,  32.89376840679705,
+      29.748949561287034,  38.28837943815329,
+      46.04345773288535,   53.5723809439155,
+      49.558046773455466, 44.384682042344295,
+      54.037024344425184,  64.42049363362563,
+      74.33034373659252,  68.33739825307956,
+      60.8276253029822, 53.907327887774215,
+      64.18722614352485,  73.95944834840239,
+      67.94115100585212,  60.41522986797286,
+      52.507142371300304, 62.369864518050704,
+      71.69379331573968,   65.7419196555744,
+      58.35237784358063,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d strides default', function() {
+    const x = new Tensor([1, 1, 5, 5], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [2, 2];
+    const strides = [2, 2];
+    const y = l2Pool2d(x, {windowDimensions, strides});
+    utils.checkShape(y, [1, 1, 2, 2]);
+    const expected = [
+      9.486832980505138,
+      13.038404810405298,
+      28.460498941515414,
+      32.4037034920393,
+    ];
+    utils.checkValue(y, expected);
+  });
+
+  it('l2Pool2d strides nhwc', function() {
+    const x = new Tensor([1, 5, 5, 1], [
+      1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13,
+      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+    ]);
+    const windowDimensions = [2, 2];
+    const strides = [2, 2];
+    const layout = 'nhwc';
+    const y = l2Pool2d(x, {windowDimensions, strides, layout});
+    utils.checkShape(y, [1, 2, 2, 1]);
+    const expected = [
+      9.486832980505138,
+      13.038404810405298,
+      28.460498941515414,
+      32.4037034920393,
+    ];
+    utils.checkValue(y, expected);
+  });
+});