diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt
index fcefba66..f1c7b96d 100644
--- a/android/CMakeLists.txt
+++ b/android/CMakeLists.txt
@@ -4,22 +4,33 @@ project(react-native-audio-context)
 set (CMAKE_VERBOSE_MAKEFILE ON)
 set (CMAKE_CXX_STANDARD 14)
 
-add_library(react-native-audio-context
-            SHARED
-            ../cpp/JSIExampleHostObject.cpp
-            cpp-adapter.cpp
-)
+include(../node_modules/react-native/ReactAndroid/cmake-utils/folly-flags.cmake)
+add_compile_options(${folly_FLAGS})
 
-# Specifies a path to native header files.
 include_directories(
-            ../cpp
-            ../node_modules/react-native/ReactCommon/jsi
+        ../cpp
+        src/main/cpp
+        ../node_modules/react-native/ReactCommon/jsi
+        ../node_modules/react-native/ReactAndroid/src/main/jni/react/jni
+        ../node_modules/react-native/ReactAndroid/src/main/jni/third-party/folly
+)
+
+add_library(react-native-audio-context SHARED
+        src/main/cpp/OnLoad.cpp
+        src/main/cpp/AudioContext
+        src/main/cpp/OscillatorNode
+        ../cpp/AudioContextHostObject
+        ../cpp/OscillatorNodeHostObject
 )
 
 find_package(ReactAndroid REQUIRED CONFIG)
+find_package(fbjni REQUIRED CONFIG)
 
-target_link_libraries(
-  react-native-audio-context
-  ReactAndroid::jsi
-  android
+target_link_libraries(react-native-audio-context
+        ReactAndroid::jsi
+        ReactAndroid::reactnativejni
+        fbjni::fbjni
+        ReactAndroid::folly_runtime
+        android
+        log
 )
diff --git a/android/build.gradle b/android/build.gradle
index 431dd98a..65874cc2 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -72,6 +72,10 @@ android {
     }
   }
 
+  packagingOptions {
+    excludes = ["**/libc++_shared.so", "**/libfbjni.so", "**/libjsi.so", "**/libreactnativejni.so", "**/libfolly_json.so", "**/libreanimated.so", "**/libjscexecutor.so", "**/libhermes.so"]
+  }
+
   externalNativeBuild {
     cmake {
       path "CMakeLists.txt"
@@ -125,6 +129,7 @@ dependencies {
   //noinspection GradleDynamicVersion
   implementation "com.facebook.react:react-native:+"
   implementation 'androidx.core:core-ktx:1.13.1'
+  implementation 'com.facebook.fbjni:fbjni:0.6.0'
 }
 
 if (isNewArchitectureEnabled()) {
diff --git a/android/cpp-adapter.cpp b/android/cpp-adapter.cpp
deleted file mode 100644
index a7b0247d..00000000
--- a/android/cpp-adapter.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#include <jni.h>
-#include <jsi/jsi.h>
-#include "JSIExampleHostObject.h"
-
-using namespace facebook;
-
-void install(jsi::Runtime& runtime) {
-    auto hostObject = std::make_shared<example::JSIExampleHostObject>();
-    auto object = jsi::Object::createFromHostObject(runtime, hostObject);
-    runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object));
-}
-
-extern "C"
-JNIEXPORT void JNICALL
-Java_com_audiocontext_jsi_JSIExampleModule_00024Companion_nativeInstall(JNIEnv *env, jobject clazz, jlong jsiPtr) {
-    auto runtime = reinterpret_cast<jsi::Runtime*>(jsiPtr);
-    if (runtime) {
-        install(*runtime);
-    }
-}
diff --git a/android/src/main/cpp/AudioContext.cpp b/android/src/main/cpp/AudioContext.cpp
new file mode 100644
index 00000000..731d04a2
--- /dev/null
+++ b/android/src/main/cpp/AudioContext.cpp
@@ -0,0 +1,28 @@
+#include "AudioContext.h"
+#include "AudioContextHostObject.h"
+#include "OscillatorNode.h"
+#include <fbjni/fbjni.h>
+#include <jsi/jsi.h>
+#include <android/log.h>
+
+namespace audiocontext {
+
+    using namespace facebook::jni;
+
+    AudioContext::AudioContext(jni::alias_ref<AudioContext::jhybridobject> &jThis,
+                                   jlong jsContext): javaObject_(make_global(jThis)) {
+        auto runtime = reinterpret_cast<jsi::Runtime *>(jsContext);
+        auto hostObject = std::make_shared<AudioContextHostObject>(this);
+        auto object = jsi::Object::createFromHostObject(*runtime, hostObject);
+        runtime->global().setProperty(*runtime, "__AudioContextProxy", std::move(object));
+    }
+
+    jsi::Object AudioContext::createOscillator() {
+        static const auto method = javaClassLocal()->getMethod<OscillatorNode()>("createOscillator");
+        auto oscillator = method(javaObject_.get());
+        auto oscillatorCppInstance = oscillator->cthis();
+
+        return oscillatorCppInstance->createOscillatorNodeHostObject();
+    }
+
+} // namespace audiocontext
diff --git a/android/src/main/cpp/AudioContext.h b/android/src/main/cpp/AudioContext.h
new file mode 100644
index 00000000..d50a1775
--- /dev/null
+++ b/android/src/main/cpp/AudioContext.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <fbjni/fbjni.h>
+#include <jsi/jsi.h>
+#include <react/jni/CxxModuleWrapper.h>
+#include <react/jni/JMessageQueueThread.h>
+#include "AudioContextHostObject.h"
+#include "OscillatorNode.h"
+
+namespace audiocontext {
+
+    using namespace facebook;
+    using namespace facebook::jni;
+
+    class AudioContext : public jni::HybridClass<AudioContext> {
+    public:
+        static auto constexpr kJavaDescriptor = "Lcom/audiocontext/context/AudioContext;";
+
+        static jni::local_ref<AudioContext::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis, jlong jsContext)
+        {
+            return makeCxxInstance(jThis, jsContext);
+        }
+
+        static void registerNatives() {
+            registerHybrid({
+               makeNativeMethod("initHybrid", AudioContext::initHybrid),
+           });
+        }
+
+        jsi::Object createOscillator();
+
+    private:
+        friend HybridBase;
+
+        global_ref<AudioContext::javaobject> javaObject_;
+        std::shared_ptr<jsi::Runtime> runtime_;
+
+        explicit AudioContext(jni::alias_ref<AudioContext::jhybridobject>& jThis, jlong jsContext);
+    };
+
+} // namespace audiocontext
diff --git a/android/src/main/cpp/OnLoad.cpp b/android/src/main/cpp/OnLoad.cpp
new file mode 100644
index 00000000..93daacda
--- /dev/null
+++ b/android/src/main/cpp/OnLoad.cpp
@@ -0,0 +1,13 @@
+#include <fbjni/fbjni.h>
+#include "OscillatorNode.h"
+#include "AudioContext.h"
+
+using namespace audiocontext;
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+  return facebook::jni::initialize(vm, [] {
+        OscillatorNode::registerNatives();
+        AudioContext::registerNatives();
+  });
+}
diff --git a/android/src/main/cpp/OscillatorNode.cpp b/android/src/main/cpp/OscillatorNode.cpp
new file mode 100644
index 00000000..5e3899b3
--- /dev/null
+++ b/android/src/main/cpp/OscillatorNode.cpp
@@ -0,0 +1,48 @@
+#include "OscillatorNode.h"
+#include <fbjni/fbjni.h>
+#include <jsi/jsi.h>
+#include <android/log.h>
+
+namespace audiocontext {
+
+    using namespace facebook::jni;
+
+    OscillatorNode::OscillatorNode(jni::alias_ref<OscillatorNode::jhybridobject> &jThis, jlong jsContext)
+            : javaObject_(make_global(jThis)), jsContext(jsContext){}
+
+    jsi::Object OscillatorNode::createOscillatorNodeHostObject() {
+        auto runtime = reinterpret_cast<jsi::Runtime *>(jsContext);
+        auto hostObject = std::make_shared<OscillatorNodeHostObject>(this);
+        return jsi::Object::createFromHostObject(*runtime, hostObject);
+    }
+
+    void OscillatorNode::start() {
+        static const auto method = javaClassStatic()->getMethod<void()>("start");
+        method(javaObject_.get());
+    }
+
+    void OscillatorNode::stop() {
+        static const auto method = javaClassStatic()->getMethod<void()>("stop");
+        method(javaObject_.get());
+    }
+
+    void OscillatorNode::setFrequency(jdouble frequency) {
+        static const auto method = javaClassStatic()->getMethod<void(jdouble)>("setFrequency");
+        method(javaObject_.get(), frequency);
+    }
+
+    void OscillatorNode::setDetune(jdouble detune) {
+        static const auto method = javaClassStatic()->getMethod<void(jdouble)>("setDetune");
+        method(javaObject_.get(), detune);
+    }
+
+    jdouble OscillatorNode::getFrequency() {
+        static const auto method = javaClassLocal()->getMethod<jdouble()>("getFrequency");
+        return method(javaObject_.get());
+    }
+
+    jdouble OscillatorNode::getDetune() {
+        static const auto method = javaClassStatic()->getMethod<jdouble()>("getDetune");
+        return method(javaObject_.get());
+    }
+} // namespace audiocontext
diff --git a/android/src/main/cpp/OscillatorNode.h b/android/src/main/cpp/OscillatorNode.h
new file mode 100644
index 00000000..8471ab17
--- /dev/null
+++ b/android/src/main/cpp/OscillatorNode.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <fbjni/fbjni.h>
+#include <jsi/jsi.h>
+#include <react/jni/CxxModuleWrapper.h>
+#include <react/jni/JMessageQueueThread.h>
+#include "OscillatorNodeHostObject.h"
+
+namespace audiocontext {
+
+    using namespace facebook;
+    using namespace facebook::jni;
+
+    class OscillatorNode : public jni::HybridClass<OscillatorNode> {
+    public:
+        static auto constexpr kJavaDescriptor = "Lcom/audiocontext/nodes/oscillator/OscillatorNode;";
+
+        static jni::local_ref<OscillatorNode::jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis, jlong jsContext)
+        {
+          return makeCxxInstance(jThis, jsContext);
+        }
+
+        static void registerNatives() {
+            registerHybrid({
+                makeNativeMethod("initHybrid", OscillatorNode::initHybrid),
+            });
+        }
+
+        void start();
+        void stop();
+        void setFrequency(jdouble frequency);
+        void setDetune(jdouble detune);
+        jdouble getFrequency();
+        jdouble getDetune();
+
+        jsi::Object createOscillatorNodeHostObject();
+
+    private:
+        friend HybridBase;
+
+        global_ref<OscillatorNode::javaobject> javaObject_;
+        jlong jsContext;
+
+        explicit OscillatorNode(jni::alias_ref<OscillatorNode::jhybridobject>& jThis, jlong jsContext);
+    };
+
+} // namespace audiocontext
diff --git a/android/src/main/java/com/audiocontext/JSIExamplePackage.kt b/android/src/main/java/com/audiocontext/AudioContextPackage.kt
similarity index 72%
rename from android/src/main/java/com/audiocontext/JSIExamplePackage.kt
rename to android/src/main/java/com/audiocontext/AudioContextPackage.kt
index 81750b80..e4cb0d94 100644
--- a/android/src/main/java/com/audiocontext/JSIExamplePackage.kt
+++ b/android/src/main/java/com/audiocontext/AudioContextPackage.kt
@@ -1,14 +1,14 @@
 package com.audiocontext
 
-import com.audiocontext.jsi.JSIExampleModule
+import com.audiocontext.nativemodules.AudioContextModule
 import com.facebook.react.ReactPackage
 import com.facebook.react.bridge.NativeModule
 import com.facebook.react.bridge.ReactApplicationContext
 import com.facebook.react.uimanager.ViewManager
 
-class JSIExamplePackage : ReactPackage {
+class AudioContextPackage : ReactPackage {
   override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
-    return listOf<NativeModule>(JSIExampleModule(reactContext))
+    return listOf<NativeModule>(AudioContextModule(reactContext))
   }
 
   override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
diff --git a/android/src/main/java/com/audiocontext/context/AudioContext.kt b/android/src/main/java/com/audiocontext/context/AudioContext.kt
new file mode 100644
index 00000000..86f02201
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/context/AudioContext.kt
@@ -0,0 +1,40 @@
+package com.audiocontext.context
+
+import android.media.AudioTrack
+import com.audiocontext.nodes.AudioDestinationNode
+import com.audiocontext.nodes.AudioNode
+import com.audiocontext.nodes.oscillator.OscillatorNode
+import com.facebook.jni.HybridData
+import com.facebook.react.bridge.ReactApplicationContext
+import java.util.concurrent.CopyOnWriteArrayList
+
+class AudioContext(private val reactContext: ReactApplicationContext) : BaseAudioContext {
+  override var sampleRate: Int = 44100
+  override val destination: AudioDestinationNode = AudioDestinationNode(this)
+  override val sources = CopyOnWriteArrayList<AudioNode>()
+
+  private val mHybridData: HybridData?;
+
+  companion object {
+    init {
+      System.loadLibrary("react-native-audio-context")
+    }
+  }
+
+  init {
+    mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get())
+  }
+
+  external fun initHybrid(l: Long): HybridData?
+
+  private fun addNode(node: AudioNode) {
+    sources.add(node)
+  }
+
+  override fun createOscillator(): OscillatorNode {
+    val oscillator = OscillatorNode(this, reactContext)
+    oscillator.connect(destination)
+    addNode(oscillator)
+    return oscillator
+  }
+}
diff --git a/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt
new file mode 100644
index 00000000..80175c5d
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/context/BaseAudioContext.kt
@@ -0,0 +1,15 @@
+package com.audiocontext.context
+
+import android.media.AudioTrack
+import com.audiocontext.nodes.AudioDestinationNode
+import com.audiocontext.nodes.AudioNode
+import com.audiocontext.nodes.oscillator.OscillatorNode
+import com.facebook.jni.HybridData
+
+interface BaseAudioContext {
+  val sampleRate: Int
+  val destination: AudioDestinationNode
+  val sources: List<AudioNode>
+
+  abstract fun createOscillator(): OscillatorNode
+}
diff --git a/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt b/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt
deleted file mode 100644
index 371c2886..00000000
--- a/android/src/main/java/com/audiocontext/jsi/JSIExampleModule.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.audiocontext.jsi
-
-import android.util.Log
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.bridge.ReactContextBaseJavaModule
-import com.facebook.react.bridge.ReactMethod
-import com.facebook.react.module.annotations.ReactModule
-
-@ReactModule(name = JSIExampleModule.NAME)
-class JSIExampleModule(reactContext: ReactApplicationContext?) :
-  ReactContextBaseJavaModule(reactContext) {
-  override fun getName(): String {
-    return NAME
-  }
-
-  @ReactMethod(isBlockingSynchronousMethod = true)
-  fun install(): Boolean {
-    try {
-      System.loadLibrary("react-native-audio-context")
-
-      val jsContext = reactApplicationContext.javaScriptContextHolder
-
-      nativeInstall(jsContext!!.get())
-      return true
-    } catch (exception: Exception) {
-      Log.e(NAME, "Failed to install JSI Bindings for react-native-audio-context", exception)
-      return false
-    }
-  }
-
-  companion object {
-    const val NAME: String = "JSIExample"
-
-    private external fun nativeInstall(jsiPtr: Long)
-  }
-}
diff --git a/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt b/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt
new file mode 100644
index 00000000..97440cb7
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nativemodules/AudioContextModule.kt
@@ -0,0 +1,18 @@
+package com.audiocontext.nativemodules
+
+import com.audiocontext.context.AudioContext
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.ReactContextBaseJavaModule
+import com.facebook.react.bridge.ReactMethod
+
+class AudioContextModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
+
+  override fun getName(): String {
+    return "AudioContextModule"
+  }
+
+  @ReactMethod(isBlockingSynchronousMethod = true)
+  fun initAudioContext() {
+    AudioContext(reactContext)
+  }
+}
diff --git a/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt
new file mode 100644
index 00000000..96c121a0
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nodes/AudioDestinationNode.kt
@@ -0,0 +1,14 @@
+package com.audiocontext.nodes
+
+import android.media.AudioTrack
+import com.audiocontext.context.BaseAudioContext
+
+
+class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) {
+  override val numberOfInputs = 1
+  override val numberOfOutputs = 0
+
+  override fun process(buffer: ShortArray, audioTrack: AudioTrack) {
+    audioTrack.write(buffer, 0, buffer.size)
+  }
+}
diff --git a/android/src/main/java/com/audiocontext/nodes/AudioNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt
new file mode 100644
index 00000000..9c68beaa
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nodes/AudioNode.kt
@@ -0,0 +1,25 @@
+package com.audiocontext.nodes
+
+import android.media.AudioTrack
+import com.audiocontext.context.BaseAudioContext
+
+
+abstract class AudioNode(val context: BaseAudioContext) {
+  abstract val numberOfInputs: Int;
+  abstract val numberOfOutputs: Int;
+  private val connectedNodes = mutableListOf<AudioNode>()
+
+  fun connect(destination: AudioNode) {
+    if(this.numberOfOutputs > 0) {
+      connectedNodes.add(destination)
+    }
+  }
+
+  fun disconnect() {
+    connectedNodes.clear()
+  }
+
+  open fun process(buffer: ShortArray, audioTrack: AudioTrack) {
+    connectedNodes.forEach { it.process(buffer, audioTrack) }
+  }
+}
diff --git a/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt b/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt
new file mode 100644
index 00000000..b2fcc5d6
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nodes/AudioScheduledSourceNode.kt
@@ -0,0 +1,8 @@
+package com.audiocontext.nodes
+
+import com.audiocontext.context.BaseAudioContext
+
+abstract class AudioScheduledSourceNode(context: BaseAudioContext) : AudioNode(context) {
+  abstract fun start()
+  abstract fun stop()
+}
diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt
new file mode 100644
index 00000000..f05373a4
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nodes/oscillator/OscillatorNode.kt
@@ -0,0 +1,120 @@
+package com.audiocontext.nodes.oscillator
+
+import android.media.AudioFormat
+import android.media.AudioManager
+import android.media.AudioTrack
+import android.util.Log
+import com.audiocontext.context.BaseAudioContext
+import com.audiocontext.nodes.AudioScheduledSourceNode
+import com.facebook.jni.HybridData
+import com.facebook.react.bridge.ReactApplicationContext
+import kotlin.math.abs
+import kotlin.math.floor
+import kotlin.math.sin
+
+class OscillatorNode(context: BaseAudioContext, reactContext: ReactApplicationContext) : AudioScheduledSourceNode(context) {
+  override val numberOfInputs: Int = 0
+  override val numberOfOutputs: Int = 1
+  private var frequency: Double = 440.0
+  private var detune: Double = 0.0
+  private var waveType: WaveType = WaveType.SINE
+
+  private val audioTrack: AudioTrack
+  @Volatile private var isPlaying: Boolean = false
+  private var playbackThread: Thread? = null
+  private var buffer: ShortArray = ShortArray(1024)
+
+  private val mHybridData: HybridData?;
+
+  companion object {
+    init {
+      System.loadLibrary("react-native-audio-context")
+    }
+  }
+
+  init {
+    mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get())
+    val bufferSize = AudioTrack.getMinBufferSize(
+      context.sampleRate,
+      AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)
+    this.audioTrack = AudioTrack(
+      AudioManager.STREAM_MUSIC, context.sampleRate, AudioFormat.CHANNEL_OUT_MONO,
+      AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM
+    )
+  }
+
+  external fun initHybrid(l: Long): HybridData?
+
+  fun getFrequency(): Double {
+    return frequency
+  }
+
+  fun getDetune(): Double {
+    return detune
+  }
+
+  fun setFrequency(frequency: Double) {
+    this.frequency = frequency
+  }
+
+  fun setDetune(detune: Double) {
+    this.detune = detune
+  }
+
+  override fun start() {
+    if(isPlaying) {
+      return
+    }
+
+    isPlaying = true
+    audioTrack.play()
+    playbackThread = Thread { generateSound() }.apply{ start()}
+  }
+
+  override fun stop() {
+    if(!isPlaying) {
+      return
+    }
+
+    isPlaying = false
+    audioTrack.stop()
+    playbackThread?.join()
+  }
+
+  private fun generateSound() {
+    var wavePhase = 0.0
+    var phaseChange: Double
+
+    while(isPlaying) {
+      phaseChange = 2 * Math.PI * (frequency + detune) / context.sampleRate
+
+      for(i in buffer.indices) {
+        buffer[i] = when(waveType) {
+          WaveType.SINE -> sineWaveBuffer(wavePhase)
+          WaveType.SQUARE -> squareWaveBuffer(wavePhase)
+          WaveType.SAWTOOTH -> sawtoothWaveBuffer(wavePhase)
+          WaveType.TRIANGLE -> triangleWaveBuffer(wavePhase)
+        }
+        wavePhase += phaseChange
+      }
+      process(buffer, audioTrack)
+    }
+    audioTrack.flush()
+  }
+
+  private fun sineWaveBuffer(wavePhase: Double): Short {
+    return (sin(wavePhase) * Short.MAX_VALUE).toInt().toShort()
+  }
+
+  private fun squareWaveBuffer(wavePhase: Double): Short {
+    return ((if (sin(wavePhase) >= 0) 1 else -1) * Short.MAX_VALUE).toShort()
+  }
+
+  private fun sawtoothWaveBuffer(wavePhase: Double): Short {
+    return ((2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) * Short.MAX_VALUE).toInt().toShort()
+  }
+
+  private fun triangleWaveBuffer(wavePhase: Double): Short {
+    return ((2 * abs(2 * (wavePhase / (2 * Math.PI) - floor(wavePhase / (2 * Math.PI) + 0.5))) - 1) * Short.MAX_VALUE).toInt().toShort()
+  }
+}
diff --git a/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt
new file mode 100644
index 00000000..5d506dca
--- /dev/null
+++ b/android/src/main/java/com/audiocontext/nodes/oscillator/WaveType.kt
@@ -0,0 +1,20 @@
+package com.audiocontext.nodes.oscillator
+
+enum class WaveType {
+  SINE,
+  SQUARE,
+  SAWTOOTH,
+  TRIANGLE;
+
+  companion object {
+    fun fromString(type: String): WaveType {
+      return when (type.uppercase()) {
+        "SINE" -> SINE
+        "SQUARE" -> SQUARE
+        "SAWTOOTH" -> SAWTOOTH
+        "TRIANGLE" -> TRIANGLE
+        else -> throw IllegalArgumentException("Unknown wave type: $type")
+      }
+    }
+  }
+}
diff --git a/cpp/AudioContextHostObject.cpp b/cpp/AudioContextHostObject.cpp
new file mode 100644
index 00000000..6a16841b
--- /dev/null
+++ b/cpp/AudioContextHostObject.cpp
@@ -0,0 +1,35 @@
+#include "AudioContextHostObject.h"
+
+namespace audiocontext {
+    using namespace facebook;
+
+    std::vector<jsi::PropNameID> AudioContextHostObject::getPropertyNames(jsi::Runtime& runtime) {
+        std::vector<jsi::PropNameID> propertyNames;
+        propertyNames.push_back(jsi::PropNameID::forUtf8(runtime, "createOscillator"));
+        return propertyNames;
+    }
+
+    jsi::Value AudioContextHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
+        auto propName = propNameId.utf8(runtime);
+
+        if(propName == "createOscillator") {
+            return createOscillator(runtime, propNameId);
+        }
+
+        throw std::runtime_error("Not yet implemented!");
+    }
+
+    void AudioContextHostObject::set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) {
+        auto propName = propNameId.utf8(runtime);
+
+        throw std::runtime_error("Not yet implemented!");
+    }
+
+    jsi::Value AudioContextHostObject::createOscillator(jsi::Runtime &runtime,
+                                                        const jsi::PropNameID &propNameId) {
+        return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
+            return audiocontext_->createOscillator();
+        });
+    }
+
+}
diff --git a/cpp/AudioContextHostObject.h b/cpp/AudioContextHostObject.h
new file mode 100644
index 00000000..af9e07fd
--- /dev/null
+++ b/cpp/AudioContextHostObject.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <jsi/jsi.h>
+#include <fbjni/fbjni.h>
+#include <fbjni/detail/Hybrid.h>
+#include "AudioContext.h"
+
+namespace audiocontext {
+    using namespace facebook;
+
+    class AudioContext;
+
+    class AudioContextHostObject : public jsi::HostObject {
+    private:
+        AudioContext* audiocontext_;
+
+    public:
+        explicit AudioContextHostObject(AudioContext* audiocontext) : audiocontext_(audiocontext) {}
+
+        jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override;
+        void set(jsi::Runtime& runtime, const jsi::PropNameID& name, const jsi::Value& value) override;
+        std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
+
+        jsi::Value createOscillator(jsi::Runtime& runtime, const jsi::PropNameID& propNameId);
+    };
+} // namespace audiocontext
diff --git a/cpp/JSIExampleHostObject.cpp b/cpp/JSIExampleHostObject.cpp
deleted file mode 100644
index bc55ccb1..00000000
--- a/cpp/JSIExampleHostObject.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "JSIExampleHostObject.h"
-#include <jsi/jsi.h>
-
-namespace example {
-  using namespace facebook;
-
-  std::vector<jsi::PropNameID> JSIExampleHostObject::getPropertyNames(jsi::Runtime &runtime)
-  {
-    std::vector<jsi::PropNameID> propertyNames;
-    propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "multiply"));
-    return propertyNames;
-  }
-
-  jsi::Value JSIExampleHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) {
-    auto propName = propNameId.utf8(runtime);
-
-    if (propName == "multiply") {
-      return jsi::Function::createFromHostFunction(runtime, propNameId, 2,
-        [this](jsi::Runtime &rt, const jsi::Value &, const jsi::Value *args, size_t count) {
-          if (count != 2) {
-            throw std::invalid_argument("multiply expects exactly two arguments");
-          }
-          return this->multiply(rt, args[0], args[1]);
-        });
-    }
-
-    throw std::runtime_error("Not yet implemented!");
-  }
-
-  void JSIExampleHostObject::set(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value)
-  {
-    auto propName = propNameId.utf8(runtime);
-    if (propName == "multiply")
-    {
-      // Do nothing
-      return;
-    }
-    throw std::runtime_error("Not yet implemented!");
-  }
-
-  jsi::Value JSIExampleHostObject::multiply(jsi::Runtime &runtime, const jsi::Value &value, const jsi::Value &value2) {
-    if (value.isNumber() && value2.isNumber()) {
-      // Extract numbers and add them
-      double result = value.asNumber() + value2.asNumber();
-      return jsi::Value(result);
-    } else {
-      // Handle other cases (e.g., one is a number and the other is a string)
-      return jsi::Value::undefined();
-    }
-  }
-}
diff --git a/cpp/JSIExampleHostObject.h b/cpp/JSIExampleHostObject.h
deleted file mode 100644
index 2ea3a4ba..00000000
--- a/cpp/JSIExampleHostObject.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef JSIEXAMPLEHOSTOBJECT_H
-#define JSIEXAMPLEHOSTOBJECT_H
-
-#include <jsi/jsi.h>
-
-namespace example
-{
-
-  using namespace facebook;
-
-  class JSI_EXPORT JSIExampleHostObject : public jsi::HostObject
-  {
-    public:
-      explicit JSIExampleHostObject() = default;
-
-    public:
-      jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
-      void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override;
-      std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
-      static jsi::Value multiply(jsi::Runtime &, const jsi::Value &value, const jsi::Value &value2);
-  };
-
-} // namespace margelo
-
-#endif /* JSIEXAMPLEHOSTOBJECT_H */
diff --git a/cpp/OscillatorNodeHostObject.cpp b/cpp/OscillatorNodeHostObject.cpp
new file mode 100644
index 00000000..9d387fcc
--- /dev/null
+++ b/cpp/OscillatorNodeHostObject.cpp
@@ -0,0 +1,84 @@
+#include "OscillatorNodeHostObject.h"
+#include <android/log.h>
+
+namespace audiocontext {
+    using namespace facebook;
+
+    std::vector<jsi::PropNameID> OscillatorNodeHostObject::getPropertyNames(jsi::Runtime& runtime) {
+        std::vector<jsi::PropNameID> propertyNames;
+        propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "start"));
+        propertyNames.push_back(jsi::PropNameID::forAscii(runtime, "stop"));
+        return propertyNames;
+    }
+
+    jsi::Value OscillatorNodeHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
+        auto propName = propNameId.utf8(runtime);
+
+        if (propName == "start") {
+            return start(runtime, propNameId);
+        }
+
+        if (propName == "stop") {
+            return stop(runtime, propNameId);
+        }
+
+        if (propName == "frequency") {
+            return frequency(runtime, propNameId);
+        }
+
+        if (propName == "detune") {
+            return detune(runtime, propNameId);
+        }
+
+        throw std::runtime_error("Prop not yet implemented!");
+    }
+
+    void OscillatorNodeHostObject::set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) {
+        auto propName = propNameId.utf8(runtime);
+
+        if (propName == "frequency") {
+            auto frequency = value.asNumber();
+            oscillator_->setFrequency(frequency);
+            return;
+        }
+
+        if (propName == "detune") {
+            auto detune = value.asNumber();
+            oscillator_->setDetune(detune);
+            return;
+        }
+
+        throw std::runtime_error("Not yet implemented!");
+    }
+
+    jsi::Value OscillatorNodeHostObject::start(jsi::Runtime& runtime, const jsi::PropNameID& propNameId) {
+        return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value {
+            oscillator_->start();
+            return jsi::Value::undefined();
+        });
+    }
+
+    jsi::Value OscillatorNodeHostObject::stop(jsi::Runtime &runtime,
+                                              const jsi::PropNameID &propNameId) {
+        return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value {
+            oscillator_->stop();
+            return jsi::Value::undefined();
+        });
+    }
+
+    jsi::Value OscillatorNodeHostObject::frequency(jsi::Runtime &runtime,
+                                                   const jsi::PropNameID &propNameId) {
+        return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value {
+            auto frequency = oscillator_->getFrequency();
+            return jsi::Value(frequency);
+        });
+    }
+
+    jsi::Value OscillatorNodeHostObject::detune(jsi::Runtime &runtime,
+                                                const jsi::PropNameID &propNameId) {
+        return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime& rt, const jsi::Value& thisValue, const jsi::Value* args, size_t count) -> jsi::Value {
+            auto detune = oscillator_->getDetune();
+            return jsi::Value(detune);
+        });
+    }
+}
diff --git a/cpp/OscillatorNodeHostObject.h b/cpp/OscillatorNodeHostObject.h
new file mode 100644
index 00000000..9a0822f9
--- /dev/null
+++ b/cpp/OscillatorNodeHostObject.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <jsi/jsi.h>
+#include <fbjni/fbjni.h>
+#include <fbjni/detail/Hybrid.h>
+#include "OscillatorNode.h"
+
+namespace audiocontext {
+    using namespace facebook;
+
+    class OscillatorNode;
+
+    class OscillatorNodeHostObject : public jsi::HostObject {
+    private:
+        OscillatorNode* oscillator_;
+
+    public:
+        explicit OscillatorNodeHostObject(OscillatorNode* oscillator) : oscillator_(oscillator) {}
+
+        jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override;
+        void set(jsi::Runtime& runtime, const jsi::PropNameID& name, const jsi::Value& value) override;
+        std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& rt) override;
+
+        jsi::Value start(jsi::Runtime& runtime, const jsi::PropNameID& propNameId);
+        jsi::Value stop(jsi::Runtime& runtime, const jsi::PropNameID& propNameId);
+        jsi::Value frequency(jsi::Runtime& runtime, const jsi::PropNameID& propNameId);
+        jsi::Value detune(jsi::Runtime& runtime, const jsi::PropNameID& propNameId);
+    };
+} // namespace audiocontext
diff --git a/example/src/App.tsx b/example/src/App.tsx
index a17ec8af..e3c9ce43 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,47 +1,55 @@
-import React from 'react';
-import { StyleSheet, View, Text } from 'react-native';
-import JSIExample from '../../src/JSIExample/JSIExample';
+/* eslint-disable react/react-in-jsx-scope */
+import { Button, StyleSheet, Text, View } from 'react-native';
+import { useRef, useEffect } from 'react';
 
-const App: React.FC = () => {
-  const multiply = () => {
-    return JSIExample.multiply(2, 3);
+import { AudioContext, type Oscillator } from 'react-native-audio-context';
+
+const App = () => {
+  const audioContextRef = useRef<AudioContext | null>(null);
+  const oscillatorRef = useRef<Oscillator | null>(null);
+  const secondaryOscillatorRef = useRef<Oscillator | null>(null);
+
+  useEffect(() => {
+    audioContextRef.current = new AudioContext();
+    oscillatorRef.current = audioContextRef.current.createOscillator();
+    secondaryOscillatorRef.current = audioContextRef.current.createOscillator();
+    secondaryOscillatorRef.current.frequency = 300;
+
+    return () => {
+      //TODO
+    };
+  }, []);
+
+  const startOscillator = () => {
+    oscillatorRef.current?.start();
+    secondaryOscillatorRef.current?.start();
+  };
+  const stopOscillator = () => {
+    oscillatorRef.current?.stop();
+    secondaryOscillatorRef.current?.stop();
   };
 
   return (
     <View style={styles.container}>
-      <Text>{multiply()}</Text>
+      <Text style={styles.title}>React Native Oscillator</Text>
+      <Button title="Start Oscillator" onPress={startOscillator} />
+      <Button title="Stop Oscillator" onPress={stopOscillator} />
     </View>
   );
 };
 
-export default App;
-
 const styles = StyleSheet.create({
   container: {
     flex: 1,
-    alignItems: 'center',
     justifyContent: 'center',
-    paddingHorizontal: 20,
-  },
-  keys: {
-    fontSize: 14,
-    color: 'grey',
-  },
-  title: {
-    fontSize: 16,
-    color: 'black',
-    marginRight: 10,
-  },
-  row: {
-    flexDirection: 'row',
     alignItems: 'center',
+    backgroundColor: '#F5FCFF',
   },
-  textInput: {
-    flex: 1,
-    marginVertical: 20,
-    borderWidth: StyleSheet.hairlineWidth,
-    borderColor: 'black',
-    borderRadius: 5,
-    padding: 10,
+  title: {
+    fontSize: 20,
+    textAlign: 'center',
+    margin: 10,
   },
 });
+
+export default App;
diff --git a/src/JSIExample/JSIExample.ts b/src/JSIExample/JSIExample.ts
deleted file mode 100644
index 8895ad3a..00000000
--- a/src/JSIExample/JSIExample.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import { NativeModules, Platform } from 'react-native';
-import type { JSIExampleWrapper } from './types';
-
-function verifyExpoGo() {
-  const ExpoConstants =
-    NativeModules.NativeUnimoduleProxy?.modulesConstants?.ExponentConstants;
-  if (ExpoConstants != null) {
-    if (ExpoConstants.appOwnership === 'expo') {
-      throw new Error(
-        'react-native-fast-crypto is not supported in Expo Go! Use EAS (`expo prebuild`) or eject to a bare workflow instead.'
-      );
-    } else {
-      throw new Error('\n* Make sure you ran `expo prebuild`.');
-    }
-  }
-}
-
-function getJSIExample() {
-  const JSIExampleModule = NativeModules.JSIExample;
-  if (JSIExampleModule == null) {
-    let message =
-      'Failed to install react-native-fast-crypto: The native `JSIExample` Module could not be found.';
-    message +=
-      '\n* Make sure react-native-fast-crypto is correctly autolinked (run `npx react-native config` to verify)';
-    if (Platform.OS === 'ios' || Platform.OS === 'macos') {
-      message += '\n* Make sure you ran `pod install` in the ios/ directory.';
-    }
-    if (Platform.OS === 'android') {
-      message += '\n* Make sure gradle is synced.';
-    }
-    message += '\n* Make sure you rebuilt the app.';
-    throw new Error(message);
-  }
-  return JSIExampleModule;
-}
-
-function verifyOnDevice(JSIExampleModule: any) {
-  if (global.nativeCallSyncHook == null || JSIExampleModule.install == null) {
-    throw new Error(
-      'Failed to install react-native-fast-crypto: React Native is not running on-device. JSIExample can only be used when synchronous method invocations (JSI) are possible. If you are using a remote debugger (e.g. Chrome), switch to an on-device debugger (e.g. Flipper) instead.'
-    );
-  }
-}
-
-function installModule(JSIExampleModule: any) {
-  const result = JSIExampleModule.install();
-  if (result !== true)
-    throw new Error(
-      `Failed to install react-native-fast-crypto: The native JSIExample Module could not be installed! Looks like something went wrong when installing JSI bindings: ${result}`
-    );
-}
-
-function verifyInstallation() {
-  if (global.__JSIExampleProxy == null)
-    throw new Error(
-      'Failed to install react-native-fast-crypto, the native initializer function does not exist. Are you trying to use JSIExample from different JS Runtimes?'
-    );
-}
-
-function createJSIExampleProxy(): JSIExampleWrapper {
-  if (global.__JSIExampleProxy) {
-    return global.__JSIExampleProxy;
-  }
-
-  verifyExpoGo();
-
-  const JSIExampleModule = getJSIExample();
-
-  verifyOnDevice(JSIExampleModule);
-  installModule(JSIExampleModule);
-  verifyInstallation();
-
-  if (global.__JSIExampleProxy == null) {
-    throw new Error('Failed to initialize __JSIExampleProxy.');
-  }
-
-  return global.__JSIExampleProxy;
-}
-
-// Call the creator and export what it returns
-export default createJSIExampleProxy();
diff --git a/src/JSIExample/types.ts b/src/JSIExample/types.ts
deleted file mode 100644
index dac41a93..00000000
--- a/src/JSIExample/types.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export interface JSIExampleWrapper {
-  multiply(a: number, b: number): number;
-}
-
-// global func declaration for JSI functions
-declare global {
-  function nativeCallSyncHook(): unknown;
-  var __JSIExampleProxy: JSIExampleWrapper | undefined;
-}
diff --git a/src/index.ts b/src/index.ts
index 0942be3c..255b48fb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,2 +1,20 @@
-export * from './JSIExample/JSIExample';
-export * from './JSIExample/types';
+import { NativeModules } from 'react-native';
+const { AudioContextModule } = NativeModules;
+import type { Oscillator, BaseAudioContext } from './types';
+
+declare global {
+  function nativeCallSyncHook(): unknown;
+  var __AudioContextProxy: BaseAudioContext;
+}
+
+export class AudioContext implements BaseAudioContext {
+  constructor() {
+    AudioContextModule.initAudioContext();
+  }
+
+  createOscillator(): Oscillator {
+    return global.__AudioContextProxy.createOscillator();
+  }
+}
+
+export type { Oscillator };
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 00000000..ee3747c1
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,22 @@
+export interface BaseAudioContext {
+  createOscillator(): Oscillator;
+}
+
+export interface AudioNode {
+  context: BaseAudioContext;
+  connect: (destination: AudioNode) => void;
+  disconnect: () => void;
+}
+
+export interface AudioScheduledSourceNode extends AudioNode {
+  start: () => void;
+  stop: () => void;
+}
+
+type WaveType = 'sine' | 'square' | 'sawtooth' | 'triangle';
+
+export interface Oscillator extends AudioScheduledSourceNode {
+  frequency: number;
+  wave: WaveType;
+  detune: number;
+}