diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md
index 34d2a58bd01..d3978958c50 100644
--- a/doc/api/perf_hooks.md
+++ b/doc/api/perf_hooks.md
@@ -817,10 +817,11 @@ added:
 -->
 
 * `options` {Object}
-  * `min` {number|bigint} The minimum recordable value. Must be an integer
+  * `lowest` {number|bigint} The lowest discernible value. Must be an integer
     value greater than 0. **Default:** `1`.
-  * `max` {number|bigint} The maximum recordable value. Must be an integer
-    value greater than `min`. **Default:** `Number.MAX_SAFE_INTEGER`.
+  * `highest` {number|bigint} The highest recordable value. Must be an integer
+    value that is equal to or greater than two times `min`.
+    **Default:** `Number.MAX_SAFE_INTEGER`.
   * `figures` {number} The number of accuracy digits. Must be a number between
     `1` and `5`. **Default:** `3`.
 * Returns {RecordableHistogram}
@@ -870,6 +871,26 @@ console.log(h.percentile(99));
 added: v11.10.0
 -->
 
+### `histogram.count`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {number}
+
+The number of samples recorded by the histogram.
+
+### `histogram.countBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The number of samples recorded by the histogram.
+
 ### `histogram.exceeds`
 
 <!-- YAML
@@ -881,6 +902,17 @@ added: v11.10.0
 The number of times the event loop delay exceeded the maximum 1 hour event
 loop delay threshold.
 
+### `histogram.exceedsBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The number of times the event loop delay exceeded the maximum 1 hour event
+loop delay threshold.
+
 ### `histogram.max`
 
 <!-- YAML
@@ -891,6 +923,16 @@ added: v11.10.0
 
 The maximum recorded event loop delay.
 
+### `histogram.maxBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The maximum recorded event loop delay.
+
 ### `histogram.mean`
 
 <!-- YAML
@@ -911,6 +953,16 @@ added: v11.10.0
 
 The minimum recorded event loop delay.
 
+### `histogram.minBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {bigint}
+
+The minimum recorded event loop delay.
+
 ### `histogram.percentile(percentile)`
 
 <!-- YAML
@@ -922,6 +974,17 @@ added: v11.10.0
 
 Returns the value at the given percentile.
 
+### `histogram.percentileBigInt(percentile)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `percentile` {number} A percentile value in the range (0, 100).
+* Returns: {bigint}
+
+Returns the value at the given percentile.
+
 ### `histogram.percentiles`
 
 <!-- YAML
@@ -932,6 +995,16 @@ added: v11.10.0
 
 Returns a `Map` object detailing the accumulated percentile distribution.
 
+### `histogram.percentilesBigInt`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* {Map}
+
+Returns a `Map` object detailing the accumulated percentile distribution.
+
 ### `histogram.reset()`
 
 <!-- YAML
@@ -990,6 +1063,16 @@ added:
   - v14.18.0
 -->
 
+### `histogram.add(other)`
+
+<!-- YAML
+added: REPLACEME
+-->
+
+* `other` {RecordableHistogram}
+
+Adds the values from `other` to this histogram.
+
 ### `histogram.record(val)`
 
 <!-- YAML
diff --git a/lib/internal/histogram.js b/lib/internal/histogram.js
index f437bfd4d79..4237e716dc0 100644
--- a/lib/internal/histogram.js
+++ b/lib/internal/histogram.js
@@ -1,10 +1,12 @@
 'use strict';
 
 const {
+  MapPrototypeEntries,
   NumberIsNaN,
   NumberIsInteger,
   NumberMAX_SAFE_INTEGER,
-  ObjectSetPrototypeOf,
+  ObjectFromEntries,
+  ReflectConstruct,
   SafeMap,
   Symbol,
 } = primordials;
@@ -24,33 +26,36 @@ const {
     ERR_ILLEGAL_CONSTRUCTOR,
     ERR_INVALID_ARG_VALUE,
     ERR_INVALID_ARG_TYPE,
+    ERR_INVALID_THIS,
     ERR_OUT_OF_RANGE,
   },
 } = require('internal/errors');
 
 const {
+  validateInteger,
   validateNumber,
+  validateObject,
+  validateUint32,
 } = require('internal/validators');
 
 const kDestroy = Symbol('kDestroy');
 const kHandle = Symbol('kHandle');
 const kMap = Symbol('kMap');
+const kRecordable = Symbol('kRecordable');
 
 const {
   kClone,
   kDeserialize,
-  JSTransferable,
+  makeTransferable,
 } = require('internal/worker/js_transferable');
 
 function isHistogram(object) {
   return object?.[kHandle] !== undefined;
 }
 
-class Histogram extends JSTransferable {
-  constructor(internal) {
-    super();
-    this[kHandle] = internal;
-    this[kMap] = new SafeMap();
+class Histogram {
+  constructor() {
+    throw new ERR_ILLEGAL_CONSTRUCTOR();
   }
 
   [kInspect](depth, options) {
@@ -68,31 +73,118 @@ class Histogram extends JSTransferable {
       mean: this.mean,
       exceeds: this.exceeds,
       stddev: this.stddev,
+      count: this.count,
       percentiles: this.percentiles,
     }, opts)}`;
   }
 
+  /**
+   * @readonly
+   * @type {number}
+   */
+  get count() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    return this[kHandle]?.count();
+  }
+
+  /**
+   * @readonly
+   * @type {bigint}
+   */
+  get countBigInt() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    return this[kHandle]?.countBigInt();
+  }
+
+  /**
+   * @readonly
+   * @type {number}
+   */
   get min() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     return this[kHandle]?.min();
   }
 
+  /**
+   * @readonly
+   * @type {bigint}
+   */
+  get minBigInt() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    return this[kHandle]?.minBigInt();
+  }
+
+  /**
+   * @readonly
+   * @type {number}
+   */
   get max() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     return this[kHandle]?.max();
   }
 
+  /**
+   * @readonly
+   * @type {bigint}
+   */
+  get maxBigInt() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    return this[kHandle]?.maxBigInt();
+  }
+
+  /**
+   * @readonly
+   * @type {number}
+   */
   get mean() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     return this[kHandle]?.mean();
   }
 
+  /**
+   * @readonly
+   * @type {number}
+   */
   get exceeds() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     return this[kHandle]?.exceeds();
   }
 
+  /**
+   * @readonly
+   * @type {bigint}
+   */
+  get exceedsBigInt() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    return this[kHandle]?.exceedsBigInt();
+  }
+
+  /**
+   * @readonly
+   * @type {number}
+   */
   get stddev() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     return this[kHandle]?.stddev();
   }
 
+  /**
+   * @param {number} percentile
+   * @returns {number}
+   */
   percentile(percentile) {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     validateNumber(percentile, 'percentile');
 
     if (NumberIsNaN(percentile) || percentile <= 0 || percentile > 100)
@@ -101,31 +193,77 @@ class Histogram extends JSTransferable {
     return this[kHandle]?.percentile(percentile);
   }
 
+  /**
+   * @param {number} percentile
+   * @returns {bigint}
+   */
+  percentileBigInt(percentile) {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    validateNumber(percentile, 'percentile');
+
+    if (NumberIsNaN(percentile) || percentile <= 0 || percentile > 100)
+      throw new ERR_INVALID_ARG_VALUE.RangeError('percentile', percentile);
+
+    return this[kHandle]?.percentileBigInt(percentile);
+  }
+
+  /**
+   * @readonly
+   * @type {Map<number,number>}
+   */
   get percentiles() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
     this[kMap].clear();
     this[kHandle]?.percentiles(this[kMap]);
     return this[kMap];
   }
 
-  reset() {
-    this[kHandle]?.reset();
+  /**
+   * @readonly
+   * @type {Map<number,bigint>}
+   */
+  get percentilesBigInt() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    this[kMap].clear();
+    this[kHandle]?.percentilesBigInt(this[kMap]);
+    return this[kMap];
   }
 
-  [kDestroy]() {
-    this[kHandle] = undefined;
+  /**
+   * @returns {void}
+   */
+  reset() {
+    if (!isHistogram(this))
+      throw new ERR_INVALID_THIS('Histogram');
+    this[kHandle]?.reset();
   }
 
   [kClone]() {
     const handle = this[kHandle];
     return {
       data: { handle },
-      deserializeInfo: 'internal/histogram:InternalHistogram'
+      deserializeInfo: 'internal/histogram:internalHistogram'
     };
   }
 
   [kDeserialize]({ handle }) {
     this[kHandle] = handle;
   }
+
+  toJSON() {
+    return {
+      count: this.count,
+      min: this.min,
+      max: this.max,
+      mean: this.mean,
+      exceeds: this.exceeds,
+      stddev: this.stddev,
+      percentiles: ObjectFromEntries(MapPrototypeEntries(this.percentiles))
+    };
+  }
 }
 
 class RecordableHistogram extends Histogram {
@@ -133,7 +271,13 @@ class RecordableHistogram extends Histogram {
     throw new ERR_ILLEGAL_CONSTRUCTOR();
   }
 
+  /**
+   * @param {number|bigint} val
+   * @returns {void}
+   */
   record(val) {
+    if (this[kRecordable] === undefined)
+      throw new ERR_INVALID_THIS('RecordableHistogram');
     if (typeof val === 'bigint') {
       this[kHandle]?.record(val);
       return;
@@ -148,56 +292,93 @@ class RecordableHistogram extends Histogram {
     this[kHandle]?.record(val);
   }
 
+  /**
+   * @returns {void}
+   */
   recordDelta() {
+    if (this[kRecordable] === undefined)
+      throw new ERR_INVALID_THIS('RecordableHistogram');
     this[kHandle]?.recordDelta();
   }
 
+  /**
+   * @param {RecordableHistogram} other
+   */
+  add(other) {
+    if (this[kRecordable] === undefined)
+      throw new ERR_INVALID_THIS('RecordableHistogram');
+    if (other[kRecordable] === undefined)
+      throw new ERR_INVALID_ARG_TYPE('other', 'RecordableHistogram', other);
+    this[kHandle]?.add(other[kHandle]);
+  }
+
   [kClone]() {
     const handle = this[kHandle];
     return {
       data: { handle },
-      deserializeInfo: 'internal/histogram:InternalRecordableHistogram'
+      deserializeInfo: 'internal/histogram:internalRecordableHistogram'
     };
   }
-}
 
-class InternalHistogram extends JSTransferable {
-  constructor(handle) {
-    super();
+  [kDeserialize]({ handle }) {
     this[kHandle] = handle;
-    this[kMap] = new SafeMap();
   }
 }
 
-class InternalRecordableHistogram extends JSTransferable {
-  constructor(handle) {
-    super();
-    this[kHandle] = handle;
-    this[kMap] = new SafeMap();
-  }
+function internalHistogram(handle) {
+  return makeTransferable(ReflectConstruct(
+    function() {
+      this[kHandle] = handle;
+      this[kMap] = new SafeMap();
+    }, [], Histogram));
 }
-
-InternalHistogram.prototype.constructor = Histogram;
-ObjectSetPrototypeOf(
-  InternalHistogram.prototype,
-  Histogram.prototype);
-
-InternalRecordableHistogram.prototype.constructor = RecordableHistogram;
-ObjectSetPrototypeOf(
-  InternalRecordableHistogram.prototype,
-  RecordableHistogram.prototype);
-
-function createHistogram() {
-  return new InternalRecordableHistogram(new _Histogram());
+internalHistogram.prototype[kDeserialize] = () => {};
+
+function internalRecordableHistogram(handle) {
+  return makeTransferable(ReflectConstruct(
+    function() {
+      this[kHandle] = handle;
+      this[kMap] = new SafeMap();
+      this[kRecordable] = true;
+    }, [], RecordableHistogram));
+}
+internalRecordableHistogram.prototype[kDeserialize] = () => {};
+
+/**
+ * @param {{
+ *   lowest? : number,
+ *   highest? : number,
+ *   figures? : number
+ * }} [options]
+ * @returns {RecordableHistogram}
+ */
+function createHistogram(options = {}) {
+  validateObject(options, 'options');
+  const {
+    lowest = 1,
+    highest = NumberMAX_SAFE_INTEGER,
+    figures = 3,
+  } = options;
+  if (typeof lowest !== 'bigint')
+    validateInteger(lowest, 'options.lowest', 1, NumberMAX_SAFE_INTEGER);
+  if (typeof highest !== 'bigint') {
+    validateInteger(highest, 'options.highest',
+                    2 * lowest, NumberMAX_SAFE_INTEGER);
+  } else if (highest < 2n * lowest) {
+    throw new ERR_INVALID_ARG_VALUE.RangeError('options.highest', highest);
+  }
+  validateUint32(figures, 'options.figures', 1, 5);
+  return internalRecordableHistogram(new _Histogram(lowest, highest, figures));
 }
 
 module.exports = {
   Histogram,
   RecordableHistogram,
-  InternalHistogram,
-  InternalRecordableHistogram,
+  internalHistogram,
+  internalRecordableHistogram,
   isHistogram,
   kDestroy,
   kHandle,
+  kMap,
   createHistogram,
 };
diff --git a/lib/internal/perf/event_loop_delay.js b/lib/internal/perf/event_loop_delay.js
index f5d0eb74d58..efd45f4ed77 100644
--- a/lib/internal/perf/event_loop_delay.js
+++ b/lib/internal/perf/event_loop_delay.js
@@ -1,16 +1,19 @@
 'use strict';
 const {
+  ReflectConstruct,
+  SafeMap,
   Symbol,
 } = primordials;
 
 const {
   codes: {
     ERR_ILLEGAL_CONSTRUCTOR,
+    ERR_INVALID_THIS,
   }
 } = require('internal/errors');
 
 const {
-  ELDHistogram: _ELDHistogram,
+  createELDHistogram,
 } = internalBinding('performance');
 
 const {
@@ -21,25 +24,38 @@ const {
 const {
   Histogram,
   kHandle,
+  kMap,
 } = require('internal/histogram');
 
+const {
+  makeTransferable,
+} = require('internal/worker/js_transferable');
+
 const kEnabled = Symbol('kEnabled');
 
 class ELDHistogram extends Histogram {
   constructor(i) {
-    if (!(i instanceof _ELDHistogram)) {
-      throw new ERR_ILLEGAL_CONSTRUCTOR();
-    }
-    super(i);
-    this[kEnabled] = false;
+    throw new ERR_ILLEGAL_CONSTRUCTOR();
   }
+
+  /**
+   * @returns {boolean}
+   */
   enable() {
+    if (this[kEnabled] === undefined)
+      throw new ERR_INVALID_THIS('ELDHistogram');
     if (this[kEnabled]) return false;
     this[kEnabled] = true;
     this[kHandle].start();
     return true;
   }
+
+  /**
+   * @returns {boolean}
+   */
   disable() {
+    if (this[kEnabled] === undefined)
+      throw new ERR_INVALID_THIS('ELDHistogram');
     if (!this[kEnabled]) return false;
     this[kEnabled] = false;
     this[kHandle].stop();
@@ -47,13 +63,24 @@ class ELDHistogram extends Histogram {
   }
 }
 
+/**
+ * @param {{
+ *   resolution : number
+ * }} [options]
+ * @returns {ELDHistogram}
+ */
 function monitorEventLoopDelay(options = {}) {
   validateObject(options, 'options');
 
   const { resolution = 10 } = options;
   validateInteger(resolution, 'options.resolution', 1);
 
-  return new ELDHistogram(new _ELDHistogram(resolution));
+  return makeTransferable(ReflectConstruct(
+    function() {
+      this[kEnabled] = false;
+      this[kHandle] = createELDHistogram(resolution);
+      this[kMap] = new SafeMap();
+    }, [], ELDHistogram));
 }
 
 module.exports = monitorEventLoopDelay;
diff --git a/src/histogram-inl.h b/src/histogram-inl.h
index 18a1668512e..3b8712c8787 100644
--- a/src/histogram-inl.h
+++ b/src/histogram-inl.h
@@ -13,35 +13,49 @@ void Histogram::Reset() {
   Mutex::ScopedLock lock(mutex_);
   hdr_reset(histogram_.get());
   exceeds_ = 0;
+  count_ = 0;
   prev_ = 0;
 }
 
-int64_t Histogram::Min() {
+double Histogram::Add(const Histogram& other) {
+  Mutex::ScopedLock lock(mutex_);
+  count_ += other.count_;
+  exceeds_ += other.exceeds_;
+  if (other.prev_ > prev_)
+    prev_ = other.prev_;
+  return static_cast<double>(hdr_add(histogram_.get(), other.histogram_.get()));
+}
+
+size_t Histogram::Count() const {
+  Mutex::ScopedLock lock(mutex_);
+  return count_;
+}
+
+int64_t Histogram::Min() const {
   Mutex::ScopedLock lock(mutex_);
   return hdr_min(histogram_.get());
 }
 
-int64_t Histogram::Max() {
+int64_t Histogram::Max() const {
   Mutex::ScopedLock lock(mutex_);
   return hdr_max(histogram_.get());
 }
 
-double Histogram::Mean() {
+double Histogram::Mean() const {
   Mutex::ScopedLock lock(mutex_);
   return hdr_mean(histogram_.get());
 }
 
-double Histogram::Stddev() {
+double Histogram::Stddev() const {
   Mutex::ScopedLock lock(mutex_);
   return hdr_stddev(histogram_.get());
 }
 
-double Histogram::Percentile(double percentile) {
+int64_t Histogram::Percentile(double percentile) const {
   Mutex::ScopedLock lock(mutex_);
   CHECK_GT(percentile, 0);
   CHECK_LE(percentile, 100);
-  return static_cast<double>(
-      hdr_value_at_percentile(histogram_.get(), percentile));
+  return hdr_value_at_percentile(histogram_.get(), percentile);
 }
 
 template <typename Iterator>
@@ -51,26 +65,31 @@ void Histogram::Percentiles(Iterator&& fn) {
   hdr_iter_percentile_init(&iter, histogram_.get(), 1);
   while (hdr_iter_next(&iter)) {
     double key = iter.specifics.percentiles.percentile;
-    double value = static_cast<double>(iter.value);
-    fn(key, value);
+    fn(key, iter.value);
   }
 }
 
 bool Histogram::Record(int64_t value) {
   Mutex::ScopedLock lock(mutex_);
-  return hdr_record_value(histogram_.get(), value);
+  bool recorded = hdr_record_value(histogram_.get(), value);
+  if (!recorded)
+    exceeds_++;
+  else
+    count_++;
+  return recorded;
 }
 
 uint64_t Histogram::RecordDelta() {
   Mutex::ScopedLock lock(mutex_);
   uint64_t time = uv_hrtime();
-  uint64_t delta = 0;
+  int64_t delta = 0;
   if (prev_ > 0) {
+    CHECK_GE(time, prev_);
     delta = time - prev_;
-    if (delta > 0) {
-      if (!hdr_record_value(histogram_.get(), delta) && exceeds_ < 0xFFFFFFFF)
-        exceeds_++;
-    }
+    if (hdr_record_value(histogram_.get(), delta))
+      count_++;
+    else
+      exceeds_++;
   }
   prev_ = time;
   return delta;
diff --git a/src/histogram.cc b/src/histogram.cc
index 6fbb0eda6c0..87641a4a369 100644
--- a/src/histogram.cc
+++ b/src/histogram.cc
@@ -10,16 +10,21 @@ namespace node {
 using v8::BigInt;
 using v8::FunctionCallbackInfo;
 using v8::FunctionTemplate;
+using v8::Integer;
 using v8::Local;
 using v8::Map;
 using v8::Number;
 using v8::Object;
 using v8::String;
+using v8::Uint32;
 using v8::Value;
 
-Histogram::Histogram(int64_t lowest, int64_t highest, int figures) {
+Histogram::Histogram(const Options& options) {
   hdr_histogram* histogram;
-  CHECK_EQ(0, hdr_init(lowest, highest, figures, &histogram));
+  CHECK_EQ(0, hdr_init(options.lowest,
+                       options.highest,
+                       options.figures,
+                       &histogram));
   histogram_.reset(histogram);
 }
 
@@ -27,8 +32,8 @@ void Histogram::MemoryInfo(MemoryTracker* tracker) const {
   tracker->TrackFieldWithSize("histogram", GetMemorySize());
 }
 
-HistogramImpl::HistogramImpl(int64_t lowest, int64_t highest, int figures)
-    : histogram_(new Histogram(lowest, highest, figures)) {}
+HistogramImpl::HistogramImpl(const Histogram::Options& options)
+    : histogram_(new Histogram(options)) {}
 
 HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> histogram)
     : histogram_(std::move(histogram)) {}
@@ -36,11 +41,9 @@ HistogramImpl::HistogramImpl(std::shared_ptr<Histogram> histogram)
 HistogramBase::HistogramBase(
     Environment* env,
     Local<Object> wrap,
-    int64_t lowest,
-    int64_t highest,
-    int figures)
+    const Histogram::Options& options)
     : BaseObject(env, wrap),
-      HistogramImpl(lowest, highest, figures) {
+      HistogramImpl(options) {
   MakeWeak();
 }
 
@@ -57,6 +60,22 @@ void HistogramBase::MemoryInfo(MemoryTracker* tracker) const {
   tracker->TrackField("histogram", histogram());
 }
 
+void HistogramBase::GetCount(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  double value = static_cast<double>((*histogram)->Count());
+  args.GetReturnValue().Set(value);
+}
+
+void HistogramBase::GetCountBigInt(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(
+      BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Count()));
+}
+
 void HistogramBase::GetMin(const FunctionCallbackInfo<Value>& args) {
   HistogramBase* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -64,6 +83,13 @@ void HistogramBase::GetMin(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void HistogramBase::GetMinBigInt(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
 void HistogramBase::GetMax(const FunctionCallbackInfo<Value>& args) {
   HistogramBase* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -71,6 +97,14 @@ void HistogramBase::GetMax(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void HistogramBase::GetMaxBigInt(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(
+      BigInt::New(env->isolate(), (*histogram)->Max()));
+}
+
 void HistogramBase::GetMean(const FunctionCallbackInfo<Value>& args) {
   HistogramBase* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -84,6 +118,14 @@ void HistogramBase::GetExceeds(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void HistogramBase::GetExceedsBigInt(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(
+      BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Exceeds()));
+}
+
 void HistogramBase::GetStddev(const FunctionCallbackInfo<Value>& args) {
   HistogramBase* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -95,7 +137,19 @@ void HistogramBase::GetPercentile(const FunctionCallbackInfo<Value>& args) {
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
   CHECK(args[0]->IsNumber());
   double percentile = args[0].As<Number>()->Value();
-  args.GetReturnValue().Set((*histogram)->Percentile(percentile));
+  double value = static_cast<double>((*histogram)->Percentile(percentile));
+  args.GetReturnValue().Set(value);
+}
+
+void HistogramBase::GetPercentileBigInt(
+    const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  CHECK(args[0]->IsNumber());
+  double percentile = args[0].As<Number>()->Value();
+  int64_t value = (*histogram)->Percentile(percentile);
+  args.GetReturnValue().Set(BigInt::New(env->isolate(), value));
 }
 
 void HistogramBase::GetPercentiles(const FunctionCallbackInfo<Value>& args) {
@@ -104,11 +158,26 @@ void HistogramBase::GetPercentiles(const FunctionCallbackInfo<Value>& args) {
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
   CHECK(args[0]->IsMap());
   Local<Map> map = args[0].As<Map>();
-  (*histogram)->Percentiles([map, env](double key, double value) {
+  (*histogram)->Percentiles([map, env](double key, int64_t value) {
+    map->Set(
+        env->context(),
+        Number::New(env->isolate(), key),
+        Number::New(env->isolate(), static_cast<double>(value))).IsEmpty();
+  });
+}
+
+void HistogramBase::GetPercentilesBigInt(
+    const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  CHECK(args[0]->IsMap());
+  Local<Map> map = args[0].As<Map>();
+  (*histogram)->Percentiles([map, env](double key, int64_t value) {
     map->Set(
         env->context(),
         Number::New(env->isolate(), key),
-        Number::New(env->isolate(), value)).IsEmpty();
+        BigInt::New(env->isolate(), value)).IsEmpty();
   });
 }
 
@@ -138,11 +207,22 @@ void HistogramBase::Record(const FunctionCallbackInfo<Value>& args) {
   (*histogram)->Record(value);
 }
 
+void HistogramBase::Add(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  HistogramBase* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+
+  CHECK(GetConstructorTemplate(env)->HasInstance(args[0]));
+  HistogramBase* other;
+  ASSIGN_OR_RETURN_UNWRAP(&other, args[0]);
+
+  double count = (*histogram)->Add(*(other->histogram()));
+  args.GetReturnValue().Set(count);
+}
+
 BaseObjectPtr<HistogramBase> HistogramBase::Create(
     Environment* env,
-    int64_t lowest,
-    int64_t highest,
-    int figures) {
+    const Histogram::Options& options) {
   Local<Object> obj;
   if (!GetConstructorTemplate(env)
           ->InstanceTemplate()
@@ -150,8 +230,7 @@ BaseObjectPtr<HistogramBase> HistogramBase::Create(
     return BaseObjectPtr<HistogramBase>();
   }
 
-  return MakeBaseObject<HistogramBase>(
-      env, obj, lowest, highest, figures);
+  return MakeBaseObject<HistogramBase>(env, obj, options);
 }
 
 BaseObjectPtr<HistogramBase> HistogramBase::Create(
@@ -169,7 +248,32 @@ BaseObjectPtr<HistogramBase> HistogramBase::Create(
 void HistogramBase::New(const FunctionCallbackInfo<Value>& args) {
   CHECK(args.IsConstructCall());
   Environment* env = Environment::GetCurrent(args);
-  new HistogramBase(env, args.This());
+
+  CHECK_IMPLIES(!args[0]->IsNumber(), args[0]->IsBigInt());
+  CHECK_IMPLIES(!args[1]->IsNumber(), args[1]->IsBigInt());
+  CHECK(args[2]->IsUint32());
+
+  int64_t lowest = 1;
+  int64_t highest = std::numeric_limits<int64_t>::max();
+
+  bool lossless_ignored;
+
+  if (args[0]->IsNumber()) {
+    lowest = args[0].As<Integer>()->Value();
+  } else if (args[0]->IsBigInt()) {
+    lowest = args[0].As<BigInt>()->Int64Value(&lossless_ignored);
+  }
+
+  if (args[1]->IsNumber()) {
+    highest = args[1].As<Integer>()->Value();
+  } else if (args[1]->IsBigInt()) {
+    highest = args[1].As<BigInt>()->Int64Value(&lossless_ignored);
+  }
+
+  int32_t figures = args[2].As<Uint32>()->Value();
+  new HistogramBase(env, args.This(), Histogram::Options {
+    lowest, highest, figures
+  });
 }
 
 Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
@@ -184,16 +288,26 @@ Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
 
     tmpl->InstanceTemplate()->SetInternalFieldCount(
         HistogramBase::kInternalFieldCount);
+    env->SetProtoMethodNoSideEffect(tmpl, "count", GetCount);
+    env->SetProtoMethodNoSideEffect(tmpl, "countBigInt", GetCountBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+    env->SetProtoMethodNoSideEffect(tmpl, "exceedsBigInt", GetExceedsBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+    env->SetProtoMethodNoSideEffect(tmpl, "minBigInt", GetMinBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+    env->SetProtoMethodNoSideEffect(tmpl, "maxBigInt", GetMaxBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
     env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
     env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+    env->SetProtoMethodNoSideEffect(tmpl, "percentileBigInt",
+                                    GetPercentileBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+    env->SetProtoMethodNoSideEffect(tmpl, "percentilesBigInt",
+                                    GetPercentilesBigInt);
     env->SetProtoMethod(tmpl, "reset", DoReset);
     env->SetProtoMethod(tmpl, "record", Record);
     env->SetProtoMethod(tmpl, "recordDelta", RecordDelta);
+    env->SetProtoMethod(tmpl, "add", Add);
     env->set_histogram_ctor_template(tmpl);
   }
   return tmpl;
@@ -202,16 +316,24 @@ Local<FunctionTemplate> HistogramBase::GetConstructorTemplate(
 void HistogramBase::RegisterExternalReferences(
     ExternalReferenceRegistry* registry) {
   registry->Register(New);
+  registry->Register(GetCount);
+  registry->Register(GetCountBigInt);
   registry->Register(GetExceeds);
+  registry->Register(GetExceedsBigInt);
   registry->Register(GetMin);
+  registry->Register(GetMinBigInt);
   registry->Register(GetMax);
+  registry->Register(GetMaxBigInt);
   registry->Register(GetMean);
   registry->Register(GetStddev);
   registry->Register(GetPercentile);
+  registry->Register(GetPercentileBigInt);
   registry->Register(GetPercentiles);
+  registry->Register(GetPercentilesBigInt);
   registry->Register(DoReset);
   registry->Register(Record);
   registry->Register(RecordDelta);
+  registry->Register(Add);
 }
 
 void HistogramBase::Initialize(Environment* env, Local<Object> target) {
@@ -242,13 +364,22 @@ Local<FunctionTemplate> IntervalHistogram::GetConstructorTemplate(
     tmpl->Inherit(HandleWrap::GetConstructorTemplate(env));
     tmpl->InstanceTemplate()->SetInternalFieldCount(
         HistogramBase::kInternalFieldCount);
+    env->SetProtoMethodNoSideEffect(tmpl, "count", GetCount);
+    env->SetProtoMethodNoSideEffect(tmpl, "countBigInt", GetCountBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "exceeds", GetExceeds);
+    env->SetProtoMethodNoSideEffect(tmpl, "exceedsBigInt", GetExceedsBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "min", GetMin);
+    env->SetProtoMethodNoSideEffect(tmpl, "minBigInt", GetMinBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "max", GetMax);
+    env->SetProtoMethodNoSideEffect(tmpl, "maxBigInt", GetMaxBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "mean", GetMean);
     env->SetProtoMethodNoSideEffect(tmpl, "stddev", GetStddev);
     env->SetProtoMethodNoSideEffect(tmpl, "percentile", GetPercentile);
+    env->SetProtoMethodNoSideEffect(tmpl, "percentileBigInt",
+                                    GetPercentileBigInt);
     env->SetProtoMethodNoSideEffect(tmpl, "percentiles", GetPercentiles);
+    env->SetProtoMethodNoSideEffect(tmpl, "percentilesBigInt",
+                                    GetPercentilesBigInt);
     env->SetProtoMethod(tmpl, "reset", DoReset);
     env->SetProtoMethod(tmpl, "start", Start);
     env->SetProtoMethod(tmpl, "stop", Stop);
@@ -259,13 +390,20 @@ Local<FunctionTemplate> IntervalHistogram::GetConstructorTemplate(
 
 void IntervalHistogram::RegisterExternalReferences(
     ExternalReferenceRegistry* registry) {
+  registry->Register(GetCount);
+  registry->Register(GetCountBigInt);
   registry->Register(GetExceeds);
+  registry->Register(GetExceedsBigInt);
   registry->Register(GetMin);
+  registry->Register(GetMinBigInt);
   registry->Register(GetMax);
+  registry->Register(GetMaxBigInt);
   registry->Register(GetMean);
   registry->Register(GetStddev);
   registry->Register(GetPercentile);
+  registry->Register(GetPercentileBigInt);
   registry->Register(GetPercentiles);
+  registry->Register(GetPercentilesBigInt);
   registry->Register(DoReset);
   registry->Register(Start);
   registry->Register(Stop);
@@ -276,24 +414,48 @@ IntervalHistogram::IntervalHistogram(
     Local<Object> wrap,
     AsyncWrap::ProviderType type,
     int32_t interval,
-    int64_t lowest,
-    int64_t highest,
-    int figures)
+    std::function<void(Histogram&)> on_interval,
+    const Histogram::Options& options)
     : HandleWrap(
           env,
           wrap,
           reinterpret_cast<uv_handle_t*>(&timer_),
           type),
-      HistogramImpl(lowest, highest, figures),
-      interval_(interval) {
+      HistogramImpl(options),
+      interval_(interval),
+      on_interval_(std::move(on_interval)) {
   MakeWeak();
   uv_timer_init(env->event_loop(), &timer_);
 }
 
+BaseObjectPtr<IntervalHistogram> IntervalHistogram::Create(
+    Environment* env,
+    int32_t interval,
+    std::function<void(Histogram&)> on_interval,
+    const Histogram::Options& options) {
+  Local<Object> obj;
+  if (!GetConstructorTemplate(env)
+          ->InstanceTemplate()
+          ->NewInstance(env->context()).ToLocal(&obj)) {
+    return BaseObjectPtr<IntervalHistogram>();
+  }
+
+  return MakeBaseObject<IntervalHistogram>(
+      env,
+      obj,
+      AsyncWrap::PROVIDER_ELDHISTOGRAM,
+      interval,
+      std::move(on_interval),
+      options);
+}
+
 void IntervalHistogram::TimerCB(uv_timer_t* handle) {
   IntervalHistogram* histogram =
       ContainerOf(&IntervalHistogram::timer_, handle);
-  histogram->OnInterval();
+
+  Histogram* h = histogram->histogram().get();
+
+  histogram->on_interval_(*h);
 }
 
 void IntervalHistogram::MemoryInfo(MemoryTracker* tracker) const {
@@ -327,6 +489,22 @@ void IntervalHistogram::Stop(const FunctionCallbackInfo<Value>& args) {
   histogram->OnStop();
 }
 
+void IntervalHistogram::GetCount(const FunctionCallbackInfo<Value>& args) {
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  double value = static_cast<double>((*histogram)->Count());
+  args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetCountBigInt(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(
+      BigInt::NewFromUnsigned(env->isolate(), (*histogram)->Count()));
+}
+
 void IntervalHistogram::GetMin(const FunctionCallbackInfo<Value>& args) {
   IntervalHistogram* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -334,6 +512,13 @@ void IntervalHistogram::GetMin(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void IntervalHistogram::GetMinBigInt(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
 void IntervalHistogram::GetMax(const FunctionCallbackInfo<Value>& args) {
   IntervalHistogram* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -341,6 +526,13 @@ void IntervalHistogram::GetMax(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void IntervalHistogram::GetMaxBigInt(const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(BigInt::New(env->isolate(), (*histogram)->Min()));
+}
+
 void IntervalHistogram::GetMean(const FunctionCallbackInfo<Value>& args) {
   IntervalHistogram* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -354,6 +546,15 @@ void IntervalHistogram::GetExceeds(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(value);
 }
 
+void IntervalHistogram::GetExceedsBigInt(
+    const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  args.GetReturnValue().Set(
+      BigInt::New(env->isolate(), (*histogram)->Exceeds()));
+}
+
 void IntervalHistogram::GetStddev(const FunctionCallbackInfo<Value>& args) {
   IntervalHistogram* histogram;
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
@@ -365,7 +566,19 @@ void IntervalHistogram::GetPercentile(const FunctionCallbackInfo<Value>& args) {
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
   CHECK(args[0]->IsNumber());
   double percentile = args[0].As<Number>()->Value();
-  args.GetReturnValue().Set((*histogram)->Percentile(percentile));
+  double value = static_cast<double>((*histogram)->Percentile(percentile));
+  args.GetReturnValue().Set(value);
+}
+
+void IntervalHistogram::GetPercentileBigInt(
+    const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  CHECK(args[0]->IsNumber());
+  double percentile = args[0].As<Number>()->Value();
+  int64_t value = (*histogram)->Percentile(percentile);
+  args.GetReturnValue().Set(BigInt::New(env->isolate(), value));
 }
 
 void IntervalHistogram::GetPercentiles(
@@ -375,11 +588,26 @@ void IntervalHistogram::GetPercentiles(
   ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
   CHECK(args[0]->IsMap());
   Local<Map> map = args[0].As<Map>();
-  (*histogram)->Percentiles([map, env](double key, double value) {
+  (*histogram)->Percentiles([map, env](double key, int64_t value) {
+    map->Set(
+        env->context(),
+        Number::New(env->isolate(), key),
+        Number::New(env->isolate(), static_cast<double>(value))).IsEmpty();
+  });
+}
+
+void IntervalHistogram::GetPercentilesBigInt(
+    const FunctionCallbackInfo<Value>& args) {
+  Environment* env = Environment::GetCurrent(args);
+  IntervalHistogram* histogram;
+  ASSIGN_OR_RETURN_UNWRAP(&histogram, args.Holder());
+  CHECK(args[0]->IsMap());
+  Local<Map> map = args[0].As<Map>();
+  (*histogram)->Percentiles([map, env](double key, int64_t value) {
     map->Set(
         env->context(),
         Number::New(env->isolate(), key),
-        Number::New(env->isolate(), value)).IsEmpty();
+        BigInt::New(env->isolate(), value)).IsEmpty();
   });
 }
 
diff --git a/src/histogram.h b/src/histogram.h
index 00b4f7796f8..d526bba8a1b 100644
--- a/src/histogram.h
+++ b/src/histogram.h
@@ -24,23 +24,29 @@ constexpr int kDefaultHistogramFigures = 3;
 
 class Histogram : public MemoryRetainer {
  public:
-  Histogram(
-      int64_t lowest = 1,
-      int64_t highest = std::numeric_limits<int64_t>::max(),
-      int figures = kDefaultHistogramFigures);
+  struct Options {
+    int64_t lowest = 1;
+    int64_t highest = std::numeric_limits<int64_t>::max();
+    int figures = kDefaultHistogramFigures;
+  };
+
+  explicit Histogram(const Options& options);
   virtual ~Histogram() = default;
 
   inline bool Record(int64_t value);
   inline void Reset();
-  inline int64_t Min();
-  inline int64_t Max();
-  inline double Mean();
-  inline double Stddev();
-  inline double Percentile(double percentile);
-  inline int64_t Exceeds() const { return exceeds_; }
+  inline int64_t Min() const;
+  inline int64_t Max() const;
+  inline double Mean() const;
+  inline double Stddev() const;
+  inline int64_t Percentile(double percentile) const;
+  inline size_t Exceeds() const { return exceeds_; }
+  inline size_t Count() const;
 
   inline uint64_t RecordDelta();
 
+  inline double Add(const Histogram& other);
+
   // Iterator is a function type that takes two doubles as argument, one for
   // percentile and one for the value at that percentile.
   template <typename Iterator>
@@ -55,20 +61,20 @@ class Histogram : public MemoryRetainer {
  private:
   using HistogramPointer = DeleteFnPtr<hdr_histogram, hdr_close>;
   HistogramPointer histogram_;
-  int64_t exceeds_ = 0;
   uint64_t prev_ = 0;
-
+  size_t exceeds_ = 0;
+  size_t count_ = 0;
   Mutex mutex_;
 };
 
 class HistogramImpl {
  public:
-  HistogramImpl(int64_t lowest, int64_t highest, int figures);
+  explicit HistogramImpl(
+      const Histogram::Options& options = Histogram::Options {});
   explicit HistogramImpl(std::shared_ptr<Histogram> histogram);
 
   Histogram* operator->() { return histogram_.get(); }
 
- protected:
   const std::shared_ptr<Histogram>& histogram() const { return histogram_; }
 
  private:
@@ -84,9 +90,7 @@ class HistogramBase : public BaseObject, public HistogramImpl {
 
   static BaseObjectPtr<HistogramBase> Create(
       Environment* env,
-      int64_t lowest = 1,
-      int64_t highest = std::numeric_limits<int64_t>::max(),
-      int figures = kDefaultHistogramFigures);
+      const Histogram::Options& options = Histogram::Options {});
 
   static BaseObjectPtr<HistogramBase> Create(
       Environment* env,
@@ -98,6 +102,12 @@ class HistogramBase : public BaseObject, public HistogramImpl {
   SET_MEMORY_INFO_NAME(HistogramBase)
   SET_SELF_SIZE(HistogramBase)
 
+  static void GetCountBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetMinBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetMaxBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetExceedsBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  static void GetCount(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMin(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMax(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMean(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -105,18 +115,21 @@ class HistogramBase : public BaseObject, public HistogramImpl {
   static void GetStddev(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetPercentile(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetPercentileBigInt(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetPercentiles(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetPercentilesBigInt(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
   static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Record(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void RecordDelta(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void Add(const v8::FunctionCallbackInfo<v8::Value>& args);
 
   HistogramBase(
       Environment* env,
       v8::Local<v8::Object> wrap,
-      int64_t lowest = 1,
-      int64_t highest = std::numeric_limits<int64_t>::max(),
-      int figures = kDefaultHistogramFigures);
+      const Histogram::Options& options = Histogram::Options {});
 
   HistogramBase(
       Environment* env,
@@ -164,23 +177,24 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
 
   static BaseObjectPtr<IntervalHistogram> Create(
       Environment* env,
-      int64_t lowest = 1,
-      int64_t highest = std::numeric_limits<int64_t>::max(),
-      int figures = kDefaultHistogramFigures);
-
-  virtual void OnInterval() = 0;
-
-  void MemoryInfo(MemoryTracker* tracker) const override;
+      int32_t interval,
+      std::function<void(Histogram&)> on_interval,
+      const Histogram::Options& options);
 
   IntervalHistogram(
       Environment* env,
       v8::Local<v8::Object> wrap,
       AsyncWrap::ProviderType type,
       int32_t interval,
-      int64_t lowest = 1,
-      int64_t highest = std::numeric_limits<int64_t>::max(),
-      int figures = kDefaultHistogramFigures);
+      std::function<void(Histogram&)> on_interval,
+      const Histogram::Options& options = Histogram::Options {});
+
+  static void GetCountBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetMinBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetMaxBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetExceedsBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
 
+  static void GetCount(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMin(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMax(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetMean(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -188,8 +202,12 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
   static void GetStddev(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetPercentile(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetPercentileBigInt(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
   static void GetPercentiles(
       const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void GetPercentilesBigInt(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
   static void DoReset(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
   static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -199,6 +217,10 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
   }
   std::unique_ptr<worker::TransferData> CloneForMessaging() const override;
 
+  void MemoryInfo(MemoryTracker* tracker) const override;
+  SET_MEMORY_INFO_NAME(IntervalHistogram)
+  SET_SELF_SIZE(IntervalHistogram)
+
  private:
   static void TimerCB(uv_timer_t* handle);
   void OnStart(StartFlags flags = StartFlags::RESET);
@@ -206,6 +228,7 @@ class IntervalHistogram : public HandleWrap, public HistogramImpl {
 
   bool enabled_ = false;
   int32_t interval_ = 0;
+  std::function<void(Histogram&)> on_interval_;
   uv_timer_t timer_;
 };
 
diff --git a/src/node_perf.cc b/src/node_perf.cc
index acbb0e0d902..8bda1791fce 100644
--- a/src/node_perf.cc
+++ b/src/node_perf.cc
@@ -234,51 +234,25 @@ void LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
   args.GetReturnValue().Set(1.0 * idle_time / 1e6);
 }
 
-// Event Loop Timing Histogram
-void ELDHistogram::New(const FunctionCallbackInfo<Value>& args) {
+void CreateELDHistogram(const FunctionCallbackInfo<Value>& args) {
   Environment* env = Environment::GetCurrent(args);
-  CHECK(args.IsConstructCall());
-  int64_t resolution = args[0].As<Integer>()->Value();
-  CHECK_GT(resolution, 0);
-  new ELDHistogram(env, args.This(), resolution);
-}
-
-void ELDHistogram::Initialize(Environment* env, Local<Object> target) {
-  Local<FunctionTemplate> tmpl = env->NewFunctionTemplate(New);
-  tmpl->Inherit(IntervalHistogram::GetConstructorTemplate(env));
-  tmpl->InstanceTemplate()->SetInternalFieldCount(
-      ELDHistogram::kInternalFieldCount);
-  env->SetConstructorFunction(target, "ELDHistogram", tmpl);
-}
-
-void ELDHistogram::RegisterExternalReferences(
-    ExternalReferenceRegistry* registry) {
-  registry->Register(New);
-  IntervalHistogram::RegisterExternalReferences(registry);
-}
-
-ELDHistogram::ELDHistogram(
-    Environment* env,
-    Local<Object> wrap,
-    int64_t interval)
-    : IntervalHistogram(
-          env,
-          wrap,
-          AsyncWrap::PROVIDER_ELDHISTOGRAM,
-          interval, 1, 3.6e12, 3) {}
-
-void ELDHistogram::OnInterval() {
-  uint64_t delta = histogram()->RecordDelta();
-  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
-                  "delay", delta);
-  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
-                 "min", histogram()->Min());
-  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
-                 "max", histogram()->Max());
-  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
-                 "mean", histogram()->Mean());
-  TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
-                 "stddev", histogram()->Stddev());
+  int64_t interval = args[0].As<Integer>()->Value();
+  CHECK_GT(interval, 0);
+  BaseObjectPtr<IntervalHistogram> histogram =
+      IntervalHistogram::Create(env, interval, [](Histogram& histogram) {
+        uint64_t delta = histogram.RecordDelta();
+        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+                        "delay", delta);
+        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+                      "min", histogram.Min());
+        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+                      "max", histogram.Max());
+        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+                      "mean", histogram.Mean());
+        TRACE_COUNTER1(TRACING_CATEGORY_NODE2(perf, event_loop),
+                      "stddev", histogram.Stddev());
+      }, Histogram::Options { 1000 });
+  args.GetReturnValue().Set(histogram->object());
 }
 
 void GetTimeOrigin(const FunctionCallbackInfo<Value>& args) {
@@ -326,6 +300,7 @@ void Initialize(Local<Object> target,
   env->SetMethod(target, "loopIdleTime", LoopIdleTime);
   env->SetMethod(target, "getTimeOrigin", GetTimeOrigin);
   env->SetMethod(target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp);
+  env->SetMethod(target, "createELDHistogram", CreateELDHistogram);
 
   Local<Object> constants = Object::New(isolate);
 
@@ -368,7 +343,6 @@ void Initialize(Local<Object> target,
                             attr).ToChecked();
 
   HistogramBase::Initialize(env, target);
-  ELDHistogram::Initialize(env, target);
 }
 
 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
@@ -380,8 +354,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
   registry->Register(LoopIdleTime);
   registry->Register(GetTimeOrigin);
   registry->Register(GetTimeOriginTimeStamp);
+  registry->Register(CreateELDHistogram);
   HistogramBase::RegisterExternalReferences(registry);
-  ELDHistogram::RegisterExternalReferences(registry);
+  IntervalHistogram::RegisterExternalReferences(registry);
 }
 }  // namespace performance
 }  // namespace node
diff --git a/src/node_perf.h b/src/node_perf.h
index 64913ab9de7..b1a99171386 100644
--- a/src/node_perf.h
+++ b/src/node_perf.h
@@ -160,23 +160,6 @@ struct GCPerformanceEntryTraits {
 
 using GCPerformanceEntry = PerformanceEntry<GCPerformanceEntryTraits>;
 
-class ELDHistogram : public IntervalHistogram {
- public:
-  static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
-  static void Initialize(Environment* env, v8::Local<v8::Object> target);
-  static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
-
-  ELDHistogram(
-      Environment* env,
-      v8::Local<v8::Object> wrap,
-      int64_t interval);
-
-  void OnInterval() override;
-
-  SET_MEMORY_INFO_NAME(ELDHistogram)
-  SET_SELF_SIZE(ELDHistogram)
-};
-
 }  // namespace performance
 }  // namespace node
 
diff --git a/test/parallel/test-perf-hooks-histogram.js b/test/parallel/test-perf-hooks-histogram.js
index a60d3a94bbc..2137c1b2a3b 100644
--- a/test/parallel/test-perf-hooks-histogram.js
+++ b/test/parallel/test-perf-hooks-histogram.js
@@ -1,54 +1,75 @@
 'use strict';
 
 const common = require('../common');
-const assert = require('assert');
+
+const {
+  ok,
+  strictEqual,
+  throws,
+} = require('assert');
+
 const {
   createHistogram,
   monitorEventLoopDelay,
 } = require('perf_hooks');
+
 const { inspect } = require('util');
 
 {
   const h = createHistogram();
 
-  assert.strictEqual(h.min, 9223372036854776000);
-  assert.strictEqual(h.max, 0);
-  assert.strictEqual(h.exceeds, 0);
-  assert(Number.isNaN(h.mean));
-  assert(Number.isNaN(h.stddev));
+  strictEqual(h.min, 9223372036854776000);
+  strictEqual(h.minBigInt, 9223372036854775807n);
+  strictEqual(h.max, 0);
+  strictEqual(h.maxBigInt, 0n);
+  strictEqual(h.exceeds, 0);
+  strictEqual(h.exceedsBigInt, 0n);
+  ok(Number.isNaN(h.mean));
+  ok(Number.isNaN(h.stddev));
+
+  strictEqual(h.count, 0);
+  strictEqual(h.countBigInt, 0n);
 
   h.record(1);
 
+  strictEqual(h.count, 1);
+  strictEqual(h.countBigInt, 1n);
+
   [false, '', {}, undefined, null].forEach((i) => {
-    assert.throws(() => h.record(i), {
+    throws(() => h.record(i), {
       code: 'ERR_INVALID_ARG_TYPE'
     });
   });
-  assert.throws(() => h.record(0, Number.MAX_SAFE_INTEGER + 1), {
+  throws(() => h.record(0, Number.MAX_SAFE_INTEGER + 1), {
     code: 'ERR_OUT_OF_RANGE'
   });
 
-  assert.strictEqual(h.min, 1);
-  assert.strictEqual(h.max, 1);
-  assert.strictEqual(h.exceeds, 0);
-  assert.strictEqual(h.mean, 1);
-  assert.strictEqual(h.stddev, 0);
+  strictEqual(h.min, 1);
+  strictEqual(h.minBigInt, 1n);
+  strictEqual(h.max, 1);
+  strictEqual(h.maxBigInt, 1n);
+  strictEqual(h.exceeds, 0);
+  strictEqual(h.mean, 1);
+  strictEqual(h.stddev, 0);
+
+  strictEqual(h.percentile(1), 1);
+  strictEqual(h.percentile(100), 1);
 
-  assert.strictEqual(h.percentile(1), 1);
-  assert.strictEqual(h.percentile(100), 1);
+  strictEqual(h.percentileBigInt(1), 1n);
+  strictEqual(h.percentileBigInt(100), 1n);
 
   const mc = new MessageChannel();
   mc.port1.onmessage = common.mustCall(({ data }) => {
-    assert.strictEqual(h.min, 1);
-    assert.strictEqual(h.max, 1);
-    assert.strictEqual(h.exceeds, 0);
-    assert.strictEqual(h.mean, 1);
-    assert.strictEqual(h.stddev, 0);
+    strictEqual(h.min, 1);
+    strictEqual(h.max, 1);
+    strictEqual(h.exceeds, 0);
+    strictEqual(h.mean, 1);
+    strictEqual(h.stddev, 0);
 
     data.record(2n);
     data.recordDelta();
 
-    assert.strictEqual(h.max, 2);
+    strictEqual(h.max, 2);
 
     mc.port1.close();
   });
@@ -57,13 +78,15 @@ const { inspect } = require('util');
 
 {
   const e = monitorEventLoopDelay();
+  strictEqual(e.count, 0);
   e.enable();
   const mc = new MessageChannel();
   mc.port1.onmessage = common.mustCall(({ data }) => {
-    assert(typeof data.min, 'number');
-    assert(data.min > 0);
-    assert.strictEqual(data.disable, undefined);
-    assert.strictEqual(data.enable, undefined);
+    strictEqual(typeof data.min, 'number');
+    ok(data.min > 0);
+    ok(data.count > 0);
+    strictEqual(data.disable, undefined);
+    strictEqual(data.enable, undefined);
     mc.port1.close();
   });
   setTimeout(() => mc.port2.postMessage(e), 100);
@@ -71,12 +94,66 @@ const { inspect } = require('util');
 
 {
   const h = createHistogram();
-  assert(inspect(h, { depth: null }).startsWith('Histogram'));
-  assert.strictEqual(inspect(h, { depth: -1 }), '[RecordableHistogram]');
+  ok(inspect(h, { depth: null }).startsWith('Histogram'));
+  strictEqual(inspect(h, { depth: -1 }), '[RecordableHistogram]');
 }
 
 {
   // Tests that RecordableHistogram is impossible to construct manually
   const h = createHistogram();
-  assert.throws(() => new h.constructor(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
+  throws(() => new h.constructor(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
+}
+
+{
+  [
+    'hello',
+    1,
+    null,
+  ].forEach((i) => {
+    throws(() => createHistogram(i), { code: 'ERR_INVALID_ARG_TYPE' });
+  });
+
+  [
+    'hello',
+    false,
+    null,
+    {},
+  ].forEach((i) => {
+    throws(() => createHistogram({ lowest: i }), {
+      code: 'ERR_INVALID_ARG_TYPE',
+    });
+    throws(() => createHistogram({ highest: i }), {
+      code: 'ERR_INVALID_ARG_TYPE',
+    });
+    throws(() => createHistogram({ figures: i }), {
+      code: 'ERR_INVALID_ARG_TYPE',
+    });
+  });
+
+  createHistogram({ lowest: 1, highest: 11, figures: 1 });
+}
+
+{
+  const h1 = createHistogram();
+  const h2 = createHistogram();
+
+  h1.record(1);
+
+  strictEqual(h2.count, 0);
+  strictEqual(h1.count, 1);
+
+  h2.add(h1);
+
+  strictEqual(h2.count, 1);
+
+  [
+    'hello',
+    1,
+    false,
+    {},
+  ].forEach((i) => {
+    throws(() => h1.add(i), {
+      code: 'ERR_INVALID_ARG_TYPE',
+    });
+  });
 }