diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift
index 7d041a631..292e6f3a5 100644
--- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift
+++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift
@@ -341,3 +341,56 @@ try test("Closure Identifiers") {
     let oid2 = closureScope()
     try expectEqual(oid1, oid2)
 }
+
+func checkArray<T>(_ array: [T]) throws where T: TypedArrayElement {
+    try expectEqual(toString(JSTypedArray(array).jsValue().object!), jsStringify(array))
+}
+
+func toString<T: JSObject>(_ object: T) -> String {
+    return object.toString!().string!
+}
+
+func jsStringify(_ array: [Any]) -> String {
+    array.map({ String(describing: $0) }).joined(separator: ",")
+}
+
+try test("TypedArray") {
+    let numbers = [UInt8](0 ... 255)
+    let typedArray = JSTypedArray(numbers)
+    try expectEqual(typedArray[12], 12)
+
+    try checkArray([0, .max, 127, 1] as [UInt8])
+    try checkArray([0, 1, .max, .min, -1] as [Int8])
+
+    try checkArray([0, .max, 255, 1] as [UInt16])
+    try checkArray([0, 1, .max, .min, -1] as [Int16])
+
+    try checkArray([0, .max, 255, 1] as [UInt32])
+    try checkArray([0, 1, .max, .min, -1] as [Int32])
+
+    try checkArray([0, .max, 255, 1] as [UInt])
+    try checkArray([0, 1, .max, .min, -1] as [Int])
+
+    let float32Array: [Float32] = [0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42]
+    let jsFloat32Array = JSTypedArray(float32Array)
+    for (i, num) in float32Array.enumerated() {
+        try expectEqual(num, jsFloat32Array[i])
+    }
+
+    let float64Array: [Float64] = [0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42]
+    let jsFloat64Array = JSTypedArray(float64Array)
+    for (i, num) in float64Array.enumerated() {
+        try expectEqual(num, jsFloat64Array[i])
+    }
+}
+
+try test("TypedArray_Mutation") {
+    let array = JSTypedArray<Int>(length: 100)
+    for i in 0..<100 {
+        array[i] = i
+    }
+    for i in 0..<100 {
+        try expectEqual(i, array[i])
+    }
+    try expectEqual(toString(array.jsValue().object!), jsStringify(Array(0..<100)))
+}
diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts
index 94e436aed..d75035cbf 100644
--- a/Runtime/src/index.ts
+++ b/Runtime/src/index.ts
@@ -8,6 +8,17 @@ type pointer = number;
 interface GlobalVariable { }
 declare const window: GlobalVariable;
 declare const global: GlobalVariable;
+let globalVariable: any;
+if (typeof globalThis !== "undefined") {
+    globalVariable = globalThis
+} else if (typeof window !== "undefined") {
+    globalVariable = window
+} else if (typeof global !== "undefined") {
+    globalVariable = global
+} else if (typeof self !== "undefined") {
+    globalVariable = self
+}
+
 
 interface SwiftRuntimeExportedFunctions {
     swjs_library_version(): number;
@@ -31,6 +42,32 @@ enum JavaScriptValueKind {
     Function = 6,
 }
 
+enum JavaScriptTypedArrayKind {
+    Int8 = 0,
+    Uint8 = 1,
+    Int16 = 2,
+    Uint16 = 3,
+    Int32 = 4,
+    Uint32 = 5,
+    BigInt64 = 6,
+    BigUint64 = 7,
+    Float32 = 8,
+    Float64 = 9,
+}
+
+type TypedArray =
+    | Int8ArrayConstructor
+    | Uint8ArrayConstructor
+    | Int16ArrayConstructor
+    | Uint16ArrayConstructor
+    | Int32ArrayConstructor
+    | Uint32ArrayConstructor
+    // | BigInt64ArrayConstructor
+    // | BigUint64ArrayConstructor
+    | Float32ArrayConstructor
+    | Float64ArrayConstructor
+
+
 type SwiftRuntimeHeapEntry = {
     id: number,
     rc: number,
@@ -41,23 +78,17 @@ class SwiftRuntimeHeap {
     private _heapNextKey: number;
 
     constructor() {
-        let _global: any;
-        if (typeof window !== "undefined") {
-            _global = window
-        } else if (typeof global !== "undefined") {
-            _global = global
-        }
         this._heapValueById = new Map();
-        this._heapValueById.set(0, _global);
+        this._heapValueById.set(0, globalVariable);
 
         this._heapEntryByValue = new Map();
-        this._heapEntryByValue.set(_global, { id: 0, rc: 1 });
+        this._heapEntryByValue.set(globalVariable, { id: 0, rc: 1 });
 
         // Note: 0 is preserved for global
         this._heapNextKey = 1;
     }
 
-    allocHeap(value: any) {
+    retain(value: any) {
         const isObject = typeof value == "object";
         const entry = this._heapEntryByValue.get(value);
         if (isObject && entry) {
@@ -72,7 +103,7 @@ class SwiftRuntimeHeap {
         return id
     }
 
-    freeHeap(ref: ref) {
+    release(ref: ref) {
         const value = this._heapValueById.get(ref);
         const isObject = typeof value == "object"
         if (isObject) {
@@ -88,7 +119,11 @@ class SwiftRuntimeHeap {
     }
 
     referenceHeap(ref: ref) {
-        return this._heapValueById.get(ref)
+        const value = this._heapValueById.get(ref)
+        if (value === undefined) {
+            throw new ReferenceError("Attempted to read invalid reference " + ref)
+        }
+        return value
     }
 }
 
@@ -130,7 +165,7 @@ export class SwiftRuntime {
                 writeValue(argument, base, base + 4, base + 8, base + 16)
             }
             let output: any;
-            const callback_func_ref = this.heap.allocHeap(function (result: any) {
+            const callback_func_ref = this.heap.retain(function (result: any) {
                 output = result
             })
             exports.swjs_call_host_function(host_func_id, argv, argc, callback_func_ref)
@@ -241,7 +276,7 @@ export class SwiftRuntime {
                 case "string": {
                     const bytes = textEncoder.encode(value);
                     writeUint32(kind_ptr, JavaScriptValueKind.String);
-                    writeUint32(payload1_ptr, this.heap.allocHeap(bytes));
+                    writeUint32(payload1_ptr, this.heap.retain(bytes));
                     writeUint32(payload2_ptr, bytes.length);
                     break;
                 }
@@ -253,13 +288,13 @@ export class SwiftRuntime {
                 }
                 case "object": {
                     writeUint32(kind_ptr, JavaScriptValueKind.Object);
-                    writeUint32(payload1_ptr, this.heap.allocHeap(value));
+                    writeUint32(payload1_ptr, this.heap.retain(value));
                     writeUint32(payload2_ptr, 0);
                     break;
                 }
                 case "function": {
                     writeUint32(kind_ptr, JavaScriptValueKind.Function);
-                    writeUint32(payload1_ptr, this.heap.allocHeap(value));
+                    writeUint32(payload1_ptr, this.heap.retain(value));
                     writeUint32(payload2_ptr, 0);
                     break;
                 }
@@ -347,7 +382,7 @@ export class SwiftRuntime {
                 host_func_id: number,
                 func_ref_ptr: pointer,
             ) => {
-                const func_ref = this.heap.allocHeap(function () {
+                const func_ref = this.heap.retain(function () {
                     return callHostFunction(host_func_id, Array.prototype.slice.call(arguments))
                 })
                 writeUint32(func_ref_ptr, func_ref)
@@ -360,7 +395,7 @@ export class SwiftRuntime {
                 const result = Reflect.construct(obj, decodeValues(argv, argc))
                 if (typeof result != "object")
                     throw Error(`Invalid result type of object constructor of "${obj}": "${result}"`)
-                writeUint32(result_obj, this.heap.allocHeap(result));
+                writeUint32(result_obj, this.heap.retain(result));
             },
             swjs_instanceof: (
                 obj_ref: ref, constructor_ref: ref,
@@ -370,8 +405,21 @@ export class SwiftRuntime {
               const constructor = this.heap.referenceHeap(constructor_ref)
               return obj instanceof constructor
             },
-            swjs_destroy_ref: (ref: ref) => {
-                this.heap.freeHeap(ref)
+            swjs_create_typed_array: (
+                kind: JavaScriptTypedArrayKind,
+                elementsPtr: pointer, length: number,
+                result_obj: pointer
+            ) => {
+                const ArrayType: TypedArray = globalVariable[JavaScriptTypedArrayKind[kind] + 'Array']
+                const array = new ArrayType(memory().buffer, elementsPtr, length);
+                // Call `.slice()` to copy the memory
+                writeUint32(result_obj, this.heap.retain(array.slice()));
+            },
+            swjs_retain: (ref: ref) => {
+                this.heap.retain(this.heap.referenceHeap(ref))
+            },
+            swjs_release: (ref: ref) => {
+                this.heap.release(ref)
             }
         }
     }
diff --git a/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift
new file mode 100644
index 000000000..beb430a5f
--- /dev/null
+++ b/Sources/JavaScriptKit/BasicObjects/JSTypedArray.swift
@@ -0,0 +1,136 @@
+//
+//  Created by Manuel Burghard. Licensed unter MIT.
+//
+
+import _CJavaScriptKit
+
+public protocol TypedArrayElement: JSValueConvertible, JSValueConstructible {
+    static var typedArrayKind: JavaScriptTypedArrayKind { get }
+    static var typedArrayClass: JSFunctionRef { get }
+}
+
+public class JSTypedArray<Element>: JSValueConvertible, ExpressibleByArrayLiteral where Element: TypedArrayElement {
+    let ref: JSObject
+    public func jsValue() -> JSValue {
+        .object(ref)
+    }
+
+    public subscript(_ index: Int) -> Element {
+        get {
+            return Element.construct(from: getJSValue(this: ref, index: Int32(index)))!
+        }
+        set {
+            setJSValue(this: ref, index: Int32(index), value: newValue.jsValue())
+        }
+    }
+    
+    // This private initializer assumes that the passed object is TypedArray
+    private init(unsafe object: JSObject) {
+        self.ref = object
+    }
+
+    public init?(_ object: JSObject) {
+        guard object.isInstanceOf(Element.typedArrayClass) else { return nil }
+        self.ref = object
+    }
+
+    public convenience init(length: Int) {
+        let jsObject = Element.typedArrayClass.new(length)
+        self.init(unsafe: jsObject)
+    }
+
+    required public convenience init(arrayLiteral elements: Element...) {
+        self.init(elements)
+    }
+
+    public convenience init(_ array: [Element]) {
+        var resultObj = JavaScriptObjectRef()
+        array.withUnsafeBufferPointer { ptr in
+            _create_typed_array(Element.typedArrayKind, ptr.baseAddress!, Int32(array.count), &resultObj)
+        }
+        self.init(unsafe: JSObject(id: resultObj))
+    }
+
+    public convenience init(_ stride: StrideTo<Element>) where Element: Strideable {
+        self.init(stride.map({ $0 }))
+    }
+}
+
+// MARK: - Int and UInt support
+
+// FIXME: Should be updated to support wasm64 when that becomes available.
+func valueForBitWidth<T>(typeName: String, bitWidth: Int, when32: T) -> T {
+    if bitWidth == 32 {
+        return when32
+    } else if bitWidth == 64 {
+        fatalError("64-bit \(typeName)s are not yet supported in JSTypedArray")
+    } else {
+        fatalError("Unsupported bit width for type \(typeName): \(bitWidth) (hint: stick to fixed-size \(typeName)s to avoid this issue)")
+    }
+}
+
+extension Int: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef {
+        valueForBitWidth(typeName: "Int", bitWidth: Int.bitWidth, when32: JSObjectRef.global.Int32Array).function!
+    }
+    public static var typedArrayKind: JavaScriptTypedArrayKind {
+        valueForBitWidth(typeName: "Int", bitWidth: Int.bitWidth, when32: .int32)
+    }
+}
+extension UInt: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef {
+        valueForBitWidth(typeName: "UInt", bitWidth: Int.bitWidth, when32: JSObjectRef.global.Uint32Array).function!
+    }
+    public static var typedArrayKind: JavaScriptTypedArrayKind {
+        valueForBitWidth(typeName: "UInt", bitWidth: UInt.bitWidth, when32: .uint32)
+    }
+}
+
+// MARK: - Concrete TypedArray classes
+
+extension Int8: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Int8Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .int8 }
+}
+extension UInt8: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Uint8Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .uint8 }
+}
+// TODO: Support Uint8ClampedArray?
+
+extension Int16: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Int16Array.function! }
+   public static var typedArrayKind: JavaScriptTypedArrayKind { .int16 }
+}
+extension UInt16: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Uint16Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .uint16 }
+}
+
+extension Int32: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Int32Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .int32 }
+}
+extension UInt32: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Uint32Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .uint32 }
+}
+
+// FIXME: Support passing BigInts across the bridge
+//extension Int64: TypedArrayElement {
+//    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.BigInt64Array.function! }
+//    public static var type: JavaScriptTypedArrayKind { .bigInt64 }
+//}
+//extension UInt64: TypedArrayElement {
+//    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.BigUint64Array.function! }
+//    public static var type: JavaScriptTypedArrayKind { .bigUint64 }
+//}
+
+extension Float32: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Float32Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .float32 }
+}
+extension Float64: TypedArrayElement {
+    public static var typedArrayClass: JSFunctionRef { JSObjectRef.global.Float64Array.function! }
+    public static var typedArrayKind: JavaScriptTypedArrayKind { .float64 }
+}
diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift
index 5955aa536..076367a5d 100644
--- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift
+++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift
@@ -34,10 +34,10 @@ public class JSObject: Equatable {
         _instanceof(id, constructor.id)
     }
 
-    static let _JS_Predef_Value_Global: UInt32 = 0
+    static let _JS_Predef_Value_Global: JavaScriptObjectRef = 0
     public static let global = JSObject(id: _JS_Predef_Value_Global)
 
-    deinit { _destroy_ref(id) }
+    deinit { _release(id) }
 
     public static func == (lhs: JSObject, rhs: JSObject) -> Bool {
         return lhs.id == rhs.id
@@ -47,3 +47,4 @@ public class JSObject: Equatable {
         .object(self)
     }
 }
+
diff --git a/Sources/JavaScriptKit/JSValueConvertible.swift b/Sources/JavaScriptKit/JSValueConvertible.swift
index eb4e27452..bc3b3491e 100644
--- a/Sources/JavaScriptKit/JSValueConvertible.swift
+++ b/Sources/JavaScriptKit/JSValueConvertible.swift
@@ -40,6 +40,10 @@ extension UInt16: JSValueConvertible {
     public func jsValue() -> JSValue { .number(Double(self)) }
 }
 
+extension UInt32: JSValueConvertible {
+  public func jsValue() -> JSValue { .number(Double(self)) }
+}
+
 extension Float: JSValueConvertible {
     public func jsValue() -> JSValue { .number(Double(self)) }
 }
diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift
index 4f45d907f..ed42417c8 100644
--- a/Sources/JavaScriptKit/XcodeSupport.swift
+++ b/Sources/JavaScriptKit/XcodeSupport.swift
@@ -72,5 +72,12 @@ import _CJavaScriptKit
         _: JavaScriptHostFuncRef,
         _: UnsafePointer<JavaScriptObjectRef>!
     ) { fatalError() }
-    func _destroy_ref(_: JavaScriptObjectRef) { fatalError() }
+    func _release(_: JavaScriptObjectRef) { fatalError() }
+    func _create_typed_array<T: TypedArrayElement>(
+        _: JavaScriptTypedArrayKind,
+        _: UnsafePointer<T>,
+        _: Int32,
+        _: UnsafeMutablePointer<JavaScriptObjectRef>!
+    ) { fatalError() }
+
 #endif
diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
index 61789e16c..9bdfc9b63 100644
--- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
+++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h
@@ -18,6 +18,19 @@ typedef enum __attribute__((enum_extensibility(closed))) {
   JavaScriptValueKindFunction = 6,
 } JavaScriptValueKind;
 
+typedef enum __attribute__((enum_extensibility(closed))) {
+    JavaScriptTypedArrayKindInt8 = 0,
+    JavaScriptTypedArrayKindUint8 = 1,
+    JavaScriptTypedArrayKindInt16 = 2,
+    JavaScriptTypedArrayKindUint16 = 3,
+    JavaScriptTypedArrayKindInt32 = 4,
+    JavaScriptTypedArrayKindUint32 = 5,
+    JavaScriptTypedArrayKindBigInt64 = 6,
+    JavaScriptTypedArrayKindBigUint64 = 7,
+    JavaScriptTypedArrayKindFloat32 = 8,
+    JavaScriptTypedArrayKindFloat64 = 9,
+} JavaScriptTypedArrayKind;
+
 typedef unsigned JavaScriptPayload1;
 typedef unsigned JavaScriptPayload2;
 typedef double JavaScriptPayload3;
@@ -94,8 +107,14 @@ _create_function(const JavaScriptHostFuncRef host_func_id,
                  const JavaScriptObjectRef *func_ref_ptr);
 
 __attribute__((__import_module__("javascript_kit"),
-               __import_name__("swjs_destroy_ref"))) extern void
-_destroy_ref(const JavaScriptObjectRef ref);
+               __import_name__("swjs_release"))) extern void
+_release(const JavaScriptObjectRef ref);
+
+__attribute__((__import_module__("javascript_kit"),
+               __import_name__("swjs_create_typed_array"))) extern void
+_create_typed_array(const JavaScriptTypedArrayKind kind,
+                    const void *elementsPtr, const int length,
+                    JavaScriptObjectRef *result_obj);
 
 #endif