diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..31ed543be7d65a9d3dae3f2715ec4064478c371d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,33 @@
+name: CI
+on: [pull_request, push]
+env:
+  CI: true
+
+jobs:
+  test:
+    name: Tests for Node ${{ matrix.node }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      fail-fast: false
+      matrix:
+        node: ["0.10", "0.12", "4", "6", "8", "10", "12", "14", "16", "18"]
+        os: [ubuntu-latest, windows-latest, macos-latest]
+
+    steps:
+      - name: Clone repository
+        uses: actions/checkout@v3
+
+      - name: Set Node.js version
+        uses: actions/setup-node@v3
+        with:
+          node-version: ${{ matrix.node }}
+
+      - run: node --version
+      - run: npm --version
+
+      - name: Install npm dependencies
+        run: npm install
+
+      - name: Run tests
+        run: npm test
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 86bd20065a767644fc2bafa4a66b76eb53850045..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: node_js
-node_js:
-  - "0.10"
-  - "0.12"
-  - "4"
-  - "6"
-  - "node"
diff --git a/README.md b/README.md
index fdee23e4510050833d3200f44e6012ae49abc149..7a3a9452ce9f3e4191ef82d81a71e4a4b5e36785 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# convert-source-map [![build status](https://secure.travis-ci.org/thlorenz/convert-source-map.svg?branch=master)](http://travis-ci.org/thlorenz/convert-source-map)
+# convert-source-map [![Build Status][ci-image]][ci-url]
 
 Converts a source-map from/to  different formats and allows adding/changing properties.
 
@@ -118,3 +118,6 @@ Returns a comment that links to an external source map via `file`.
 By default, the comment is formatted like: `//# sourceMappingURL=...`, which you would normally see in a JS source file.
 
 When `options.multiline == true`, the comment is formatted like: `/*# sourceMappingURL=... */`, which you would find in a CSS source file.
+
+[ci-url]: https://github.com/thlorenz/convert-source-map/actions?query=workflow:ci
+[ci-image]: https://img.shields.io/github/workflow/status/thlorenz/convert-source-map/CI?style=flat-square
diff --git a/index.js b/index.js
index d3265f0ac5c046f406c1ec8d48f701db30e64c7f..dc602d7c6dc2780c63531cf468bcf140a9b99dca 100644
--- a/index.js
+++ b/index.js
@@ -1,7 +1,6 @@
 'use strict';
 var fs = require('fs');
 var path = require('path');
-var SafeBuffer = require('safe-buffer');
 
 Object.defineProperty(exports, 'commentRegex', {
   get: function getCommentRegex () {
@@ -16,9 +15,30 @@ Object.defineProperty(exports, 'mapFileCommentRegex', {
   }
 });
 
+var decodeBase64;
+if (typeof Buffer !== 'undefined') {
+  if (typeof Buffer.from === 'function') {
+    decodeBase64 = decodeBase64WithBufferFrom;
+  } else {
+    decodeBase64 = decodeBase64WithNewBuffer;
+  }
+} else {
+  decodeBase64 = decodeBase64WithAtob;
+}
+
+function decodeBase64WithBufferFrom(base64) {
+  return Buffer.from(base64, 'base64').toString();
+}
+
+function decodeBase64WithNewBuffer(base64) {
+  if (typeof value === 'number') {
+    throw new TypeError('The value to decode must not be of type number.');
+  }
+  return new Buffer(base64, 'base64').toString();
+}
 
-function decodeBase64(base64) {
-  return (SafeBuffer.Buffer.from(base64, 'base64') || "").toString();
+function decodeBase64WithAtob(base64) {
+  return decodeURIComponent(escape(atob(base64)));
 }
 
 function stripComment(sm) {
@@ -56,10 +76,33 @@ Converter.prototype.toJSON = function (space) {
   return JSON.stringify(this.sourcemap, null, space);
 };
 
-Converter.prototype.toBase64 = function () {
+if (typeof Buffer !== 'undefined') {
+  if (typeof Buffer.from === 'function') {
+    Converter.prototype.toBase64 = encodeBase64WithBufferFrom;
+  } else {
+    Converter.prototype.toBase64 = encodeBase64WithNewBuffer;
+  }
+} else {
+  Converter.prototype.toBase64 = encodeBase64WithBtoa;
+}
+
+function encodeBase64WithBufferFrom() {
   var json = this.toJSON();
-  return (SafeBuffer.Buffer.from(json, 'utf8') || "").toString('base64');
-};
+  return Buffer.from(json, 'utf8').toString('base64');
+}
+
+function encodeBase64WithNewBuffer() {
+  var json = this.toJSON();
+  if (typeof json === 'number') {
+    throw new TypeError('The json to encode must not be of type number.');
+  }
+  return new Buffer(json, 'utf8').toString('base64');
+}
+
+function encodeBase64WithBtoa() {
+  var json = this.toJSON();
+  return btoa(unescape(encodeURIComponent(json)));
+}
 
 Converter.prototype.toComment = function (options) {
   var base64 = this.toBase64();
diff --git a/package.json b/package.json
index 07ff61fa2a6c7b30c7c73fec16190e362705d918..0d796cacdf903ec72ba5f3a7f706e140924e4a76 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "convert-source-map",
-  "version": "1.8.0",
+  "version": "1.9.0",
   "description": "Converts a source-map from/to  different formats and allows adding/changing properties.",
   "main": "index.js",
   "scripts": {
@@ -11,9 +11,6 @@
     "url": "git://github.com/thlorenz/convert-source-map.git"
   },
   "homepage": "https://github.com/thlorenz/convert-source-map",
-  "dependencies": {
-    "safe-buffer": "~5.1.1"
-  },
   "devDependencies": {
     "inline-source-map": "~0.6.2",
     "tap": "~9.0.0"