diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index be0ef3f..0eb68ba 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -12,12 +12,12 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [10.x, 12.x, 14.x]
+        node-version: [ 18, 20, 22 ]
 
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
       - name: Use Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v1
+        uses: actions/setup-node@v4
         with:
           node-version: ${{ matrix.node-version }}
       - run: npm install
diff --git a/.gitignore b/.gitignore
index 6d395a9..c2a8fad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,13 @@
 *.min.js.gz
+*.mjs
+*.tgz
 *~
 .DS_Store
-.idea
+.idea/
+.vscode/
 .nyc_output/
 gh-pages
 node_modules
 npm-debug.log*
 package-lock.json
+~$*
diff --git a/Makefile b/Makefile
index 5746dc1..daa00a1 100755
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,17 @@
 #!/usr/bin/env bash -c make
 
 SRC=./int64-buffer.js
-TESTS=*.json ./test/*.js
+JSTEST=./test/test.js
 DIST=./dist
 JSDEST=./dist/int64-buffer.min.js
 JSGZIP=./dist/int64-buffer.min.js.gz
+ESMDEST=./int64-buffer.mjs
+ESMTEST=./test/test.mjs
 
-all: $(JSGZIP)
+all: $(JSGZIP) $(ESMDEST)
 
 clean:
-	rm -fr $(JSDEST) $(JSGZIP)
+	rm -fr $(JSDEST) $(JSGZIP) $(ESMDEST) $(ESMTEST)
 
 $(DIST):
 	mkdir -p $@
@@ -27,10 +29,23 @@ test-coverage:
 	./node_modules/.bin/nyc make mocha
 	./node_modules/.bin/nyc report --reporter=text-lcov > .nyc_output/lcov.info
 
-mocha:
-	./node_modules/.bin/mocha -R spec $(TESTS)
+mocha: $(JSTEST) $(ESMTEST)
+	./node_modules/.bin/mocha -R spec $(JSTEST)
+	./node_modules/.bin/mocha -R spec $(ESMTEST)
 
 jshint:
 	./node_modules/.bin/jshint .
 
-.PHONY: all clean test jshint mocha
+#### ES Module
+
+$(ESMDEST): $(SRC) Makefile
+	mkdir -p $(dir $@)
+	perl -pe 's#^(var|.*export)#/// $$1#; s#^(\s*)(\S.*= )(factory.")#$$1export const $$2/* \@__PURE__ */ $$3#' < $< > $@
+
+$(ESMTEST): $(JSTEST) Makefile
+	mkdir -p $(dir $@)
+	perl -pe 's#^(var exported)#/// $$1#; s#^.*#import * as exported from "../int64-buffer.mjs";# if $$. == 1' < $< > $@
+
+####
+
+.PHONY: all clean test jshint mocha esm
diff --git a/README.md b/README.md
index 2f920bf..201c8df 100644
--- a/README.md
+++ b/README.md
@@ -5,26 +5,27 @@
 [![npm version](https://badge.fury.io/js/int64-buffer.svg)](https://www.npmjs.com/package/int64-buffer)
 [![Node.js CI](https://github.com/kawanet/int64-buffer/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/kawanet/int64-buffer/actions/)
 [![Coverage Status](https://coveralls.io/repos/github/kawanet/int64-buffer/badge.svg?branch=master)](https://coveralls.io/github/kawanet/int64-buffer?branch=master)
+[![gzip size](https://img.badgesize.io/https://unpkg.com/int64-buffer/dist/int64-buffer.min.js?compression=gzip)](https://unpkg.com/int64-buffer/dist/int64-buffer.min.js)
 
-JavaScript's number based on IEEE-754 could only handle [53 bits](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) precision.
-This module provides two pair of classes: `Int64BE`/`Uint64BE` and `Int64LE`/`Uint64LE` which could hold 64 bits long integer and loose no bit.
+JavaScript's number type, based on IEEE-754, can only handle [53 bits](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) of precision.
+This module provides two pairs of classes: `Int64BE`/`Uint64BE` and `Int64LE`/`Uint64LE`, which can hold 64-bit long integers without losing any bits.
 
 ### Features
 
-- `Int64BE`/`Int64LE` for signed integer, `Uint64BE`/`Uint64LE` for unsigned.
+- `Int64BE`/`Int64LE` for signed integers, `Uint64BE`/`Uint64LE` for unsigned integers.
 - `Int64BE`/`Uint64BE` for big-endian, `Int64LE`/`Uint64LE` for little-endian.
 - `Buffer`/`Uint8Array`/`Array`/`Array`-like storage of 8 bytes length with offset.
-- No mathematical methods provided, such as `add()`, `sub()`, `mul()`, `div()` etc.
-- Optimized only for 64 bits. If you need Int128, use [bignum](https://www.npmjs.com/package/bignum) etc.
-- Small. 3KB when minified. No other module required. Portable pure JavaScript.
-- [Tested](https://github.com/kawanet/int64-buffer/actions/) on node.js v10, v12, v14 and Web browsers.
+- No mathematical methods provided, such as `add()`, `sub()`, `mul()`, `div()`, etc.
+- Optimized only for 64 bits. If you need Int128, use [bignum](https://www.npmjs.com/package/bignum) or similar libraries.
+- Small. 3KB when minified. No other modules required. Portable pure JavaScript.
+- [Tested](https://github.com/kawanet/int64-buffer/actions/) on node.js v18, v20, v22 and Web browsers.
 
 ### Usage
 
-`Int64BE` is the class to host a 64 bit signed long integer `int64_t`.
+`Int64BE` is the class to host a 64-bit signed long integer `int64_t`.
 
 ```js
-const {Int64BE} = require("int64-buffer");
+import {Int64BE} from "int64-buffer";
 
 const big = new Int64BE(-1);
 
@@ -35,10 +36,10 @@ console.log(big.toBuffer()); // <Buffer ff ff ff ff ff ff ff ff>
 
 It uses `Buffer` on Node.js and `Uint8Array` on modern Web browsers.
 
-`Uint64BE` is the class to host a 64 bit unsigned positive long integer `uint64_t`.
+`Uint64BE` is the class to host a 64-bit unsigned positive long integer `uint64_t`.
 
 ```js
-const {Uint64BE} = require("int64-buffer");
+import {Uint64BE} from "int64-buffer";
 
 const big = new Uint64BE(Math.pow(2, 63)); // a big number with 64 bits
 
@@ -47,7 +48,7 @@ console.log(big - 0); // 9223372036854776000 = IEEE-754 loses last bits
 console.log(big + ""); // "9223372036854775808" = perfectly correct
 ```
 
-`Int64LE` and `Uint64LE` work as same as above but with little-endian storage.
+`Int64LE` and `Uint64LE` work the same way as above but with little-endian storage.
 
 ### Input Constructor
 
@@ -210,7 +211,7 @@ console.log(big.toArray()); // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
 
 ### The MIT License (MIT)
 
-Copyright (c) 2015-2021 Yusuke Kawasaki
+Copyright (c) 2015-2024 Yusuke Kawasaki
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/package.json b/package.json
index 967c44f..540ba6d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "int64-buffer",
   "description": "64bit Long Integer on Buffer/Array/ArrayBuffer in Pure JavaScript",
-  "version": "1.0.2",
+  "version": "1.1.0",
   "author": "@kawanet",
   "bugs": {
     "url": "https://github.com/kawanet/int64-buffer/issues"
@@ -12,22 +12,25 @@
     "Jan Krems <jan.krems@groupon.com>"
   ],
   "devDependencies": {
-    "@types/node": "^14.14.44",
-    "coveralls": "^3.1.0",
-    "jshint": "^2.12.0",
-    "mocha": "^8.4.0",
-    "nyc": "^15.1.0",
-    "terser": "^5.7.0"
+    "@types/node": "^22.10.2",
+    "coveralls": "^3.1.1",
+    "jshint": "^2.13.6",
+    "mocha": "^11.0.1",
+    "nyc": "^17.1.0",
+    "terser": "^5.37.0"
   },
-  "engines": {
-    "node": ">= 4.5.0"
+  "exports": {
+    "types": "./int64-buffer.d.ts",
+    "import": "./int64-buffer.mjs",
+    "require": "./int64-buffer.js"
   },
   "files": [
     "LICENSE",
     "README.md",
     "dist/int64-buffer.min.js",
     "int64-buffer.js",
-    "int64-buffer.d.ts"
+    "int64-buffer.d.ts",
+    "int64-buffer.mjs"
   ],
   "homepage": "https://github.com/kawanet/int64-buffer",
   "jshintConfig": {
@@ -68,5 +71,6 @@
     "prepack": "make clean all test",
     "test": "make test"
   },
+  "type": "commonjs",
   "typings": "int64-buffer.d.ts"
 }