● Official Release

Ambient Launcher

A dedicated utility environment for Minecraft Bedrock on Android. Zero memory leaks, zero input delay. Just Minecraft, the way it should run.

Everything you need.
Nothing you don't.

Version Selector
swap_vert

Version Selector

Switch between game versions instantly with isolated saves, mods, and settings.

Shader Loader

Shader Loader

Apply and swap shader packs in seconds.

Pack Manager

Pack Manager

View and manage your packs with ease.

World Manager

World Manager

Duplicate, backup, import and export worlds.

Playtime Stats

Playtime Stats

Track sessions, time and per-world activity.

Mod Loader
extension

Curseforge Integration

Install and manage mods inside Ambient without extra tools.

More tools built in.

NBT Data Editor

NBT Data Editor

Structure Extractor

Structure Extractor

Skin Loader

Skin Loader

Resourcepack Import

Resourcepack Import

In-game Stats

In-game Stats

Client Logs

Client Logs

Storage & Paths

Storage is pre-defined for stability. Toggling Internal/External storage is not supported — this is expected. All game files remain accessible for manual backup or modding.

/Android/media/io.kitsuri.mayape/games

You may need a file manager that can access Android/media — Shizuku + ZArchiver or similar works on modern Android.

Known Bugs

  • Persistence: Settings reset when toggling between AmbientUI and OreUI.
  • Resource Packs: Imported packs expose textures in gallery apps. Fix: add an empty .nomedia file in Android/media/io.kitsuri.mayape/.
  • Add-ons: .mcaddon files aren't supported yet — force load them manually.

Changelog

V1.0.4 Hotfix
Ambient: Pocket Edition
Bug Fixes
  • Fixed Marketplace issues
  • Fixed Dressing Room / Skins not working
  • Fixed servers not loading
  • Fixed key mapper crash
  • Fixed shader loader for version 1.26.10.4
Features & Improvements
  • Added more customization options
  • Added keymap button lock
  • Improved overall performance
API Changes
  • Added RenderAPI::Register(cb fun ptr)
  • Added RenderAPI::Unregister(cb fun ptr)
  • Added TouchAPI::RegisterCallback(cb)
  • Added TouchAPI::UnregisterCallback(cb)
  • Added KeyApi::RegisterHandler(handler)
  • Added KeyApi::UnregisterHandler(handler)
  • Added TouchEvent fields: action pointerId x y
Removals
  • Removed Shader auto-fixer — removed to align with YSS changes.
● For Developers

API Reference

JS API overview and Native API references for Ambient mod development. Primary template: Lodingglue/nise-api

JS: Logging

log(message)

Routes informational messages through the client logger (tag: "JS").

log("Script initialized successfully.");

logError(message)

Logs an error message to the client logger.

logError("Hook failed at address 0x" + addr.toString(16));

logWarn(message)

Logs a warning message to the client logger.

logWarn("Symbol not found, using fallback.");

JS: Utility Functions

sleep(milliseconds)

Pauses execution of the current script for the specified duration.

log("Waiting 500ms...");
sleep(500);
log("Done.");

readFile(path)

Reads the entire contents of a file from the real filesystem. Returns ArrayBuffer.

const data = readFile("/sdcard/MyMod/patch.bin");
Hook.writeMemory(targetAddr, data);

writeFile(path, data)

Writes data to a file on the real filesystem. Returns boolean true on success.

writeFile("/sdcard/MyMod/log.txt", "hello world");
const buf = new Uint8Array([0x90, 0x90, 0x90, 0x90]).buffer;
writeFile("/sdcard/MyMod/patch.bin", buf);

fileExists(path)

Checks whether a file exists on the real filesystem.

if (fileExists("/sdcard/MyMod/config.json")) {
  const cfg = readFile("/sdcard/MyMod/config.json");
}

JS: Path Functions

getScriptPath()

Returns the absolute file path of the currently executing script.

log("Running from: " + getScriptPath());

getScriptDir()

Returns the directory that contains the currently executing script.

const dir = getScriptDir();
const assetPath = joinPath(dir, "assets", "texture.webp");

joinPath(base, ...parts)

Joins two or more path segments into a single path string.

joinPath("/sdcard/MyMod", "assets", "texture.webp");
// => "/sdcard/MyMod/assets/texture.webp"

JS: Virtual Assets

VirtualAssets.blockFile(path)

Blocks an asset path so the game cannot open it.

VirtualAssets.blockFile("assets/music/game/creative.ogg");

VirtualAssets.unblockFile(path)

Lifts a block previously applied by blockFile().

VirtualAssets.unblockFile("textures/terrain/grass.webp");

VirtualAssets.addFile(path, data)

Injects a binary file (ArrayBuffer) or string into the virtual asset registry.

const buf = readFile("/sdcard/MyMod/custom_grass.webp");
VirtualAssets.addFile("textures/terrain/grass.webp", buf);

VirtualAssets.addTextFile(path, content)

Convenience wrapper for injecting a plain-text asset.

VirtualAssets.addTextFile("config/mod_settings.json", JSON.stringify({ enabled: true }));

VirtualAssets.removeFile(path)

Removes a virtual entry from the registry.

VirtualAssets.removeFile("textures/terrain/grass.webp");

VirtualAssets.hasFile(path)

Returns whether a virtual entry is registered for the path.

if (!VirtualAssets.hasFile("shaders/glsl/terrain.vertex")) {
  VirtualAssets.addTextFile("shaders/glsl/terrain.vertex", shaderSrc);
}

VirtualAssets.loadDir(storageDir, virtualBaseDir, recursive = false)

Bulk-registers all files in an on-device directory as virtual assets. Returns number of files registered.

const count = VirtualAssets.loadDir("/sdcard/MyMod/textures", "textures", true);
log("Loaded " + count + " assets.");

VirtualAssets.replaceFile(virtualPath, storagePath)

Points a virtual asset entry at an on-device file. Returns boolean true on success.

VirtualAssets.replaceFile("textures/terrain/grass.webp", "/sdcard/MyMod/grass.webp");

VirtualAssets.readFile(path)

Reads a virtual asset into an ArrayBuffer. Returns null if not found/blocked.

const data = VirtualAssets.readFile("config/mod_settings.json");
if (data) {
  const text = new TextDecoder().decode(data);
  const cfg  = JSON.parse(text);
}

JS: Hooking

Hook.hookAddr(name, targetAddr, hookFunc, originalFunc, hookType, priority)

Installs an inline hook at targetAddr. Returns trampoline address on success; 0 on failure.

const base = Hook.getBaseAddr();
const target = base + 0x1A2B3C;
const tramp = Hook.hookAddr(
  "myHook", target, myReplacementFn, origPtr, 
  Hook.Type.INLINE, Hook.Priority.HIGH
);
if (tramp === 0) logError("Hook failed.");

Hook.unhookAddr(name)

Removes a hook installed by hookAddr() and restores the original bytes.

Hook.unhookAddr("myHook");

Hook.patchNop(name, addr, size = 4)

Overwrites size bytes at addr with NOP instructions, saving the originals.

Hook.patchNop("disableBoundsCheck", Hook.getBaseAddr() + 0xDEAD, 8);

Hook.restoreNopPatch(name)

Restores the original bytes that were overwritten by patchNop().

Hook.restoreNopPatch("disableBoundsCheck");

Hook.writeMemory(addr, data)

Writes raw bytes from an ArrayBuffer to an arbitrary memory address.

const nop = new Uint8Array([0x1F, 0x20, 0x03, 0xD5]).buffer; // 4-byte ARM64 NOP
Hook.writeMemory(Hook.getBaseAddr() + 0xCAFE, nop);

Hook.readMemory(addr, size)

Reads size bytes from an arbitrary memory address into an ArrayBuffer.

const bytes = Hook.readMemory(Hook.getBaseAddr() + 0xCAFE, 16);
if (bytes) {
  const view = new Uint8Array(bytes);
  log("First byte: 0x" + view[0].toString(16));
}

Hook.getAddress(baseAddr, offsets)

Resolves a multi-level pointer chain.

const healthAddr = Hook.getAddress(Hook.getBaseAddr(), [0x10, 0x28, 0x08]);

Hook.getBaseAddr()

Returns the cached base load address of libminecraft.so.

const base = Hook.getBaseAddr();

Hook.getModuleAddr(moduleName, permissions = "")

Finds the base address of any named module from /proc/self/maps.

const fmodBase = Hook.getModuleAddr("libfmod.so");
const fmodExec = Hook.getModuleAddr("libfmod.so", "r-xp");

Hook.getProtection(addr)

Returns the current mmap protection flags for the page at addr.

const prot = Hook.getProtection(Hook.getBaseAddr());
log("Protection flags: " + prot.toString(16));

Hook.sigScanSetup(signature, libName, flags = 0)

Prepares a signature scan using an IDA-style pattern string.

const handle = Hook.sigScanSetup("48 8B 05 ?? ?? ?? ?? 48 85 C0", "libminecraft.so");

Hook.getSigScanResult(handle)

Executes the prepared scan and returns the address of the first match.

const result = Hook.getSigScanResult(handle);
if (result) log("Pattern found at: 0x" + result.toString(16));

Hook.sigScanCleanup(handle)

Frees all resources associated with a scan handle (always call this).

Hook.sigScanCleanup(handle);

Hook.debugStatus()

Emits a debug dump of the current hook table to the log.

Hook.debugStatus();

JS: System API

System_loadLibrary(libraryId, path, flags)

Loads a shared library (.so) and registers it under a string ID.

if (!System_isLibraryLoaded("mylib")) {
  const ok = System_loadLibrary("mylib", "/data/local/tmp/mylib.so");
  if (!ok) logError(System_getLastError());
}

System_unloadLibrary(libraryId)

Unloads a managed library and removes it from the registry.

System_unloadLibrary("mylib");

System_isLibraryLoaded(libraryId)

Checks whether a library is currently loaded in the registry.

if (System_isLibraryLoaded("mylib")) {
  log("mylib is ready.");
}

System_getSymbol(libraryId, symbolName)

Looks up an exported symbol in a specific registered library.

const addr = System_getSymbol("mylib", "myExportedFunction");
if (addr) Hook.hookAddr("myHook", addr, replacement, orig);

System_getSymbolAddress(symbolName)

Searches all registered libraries for a symbol and returns the first match.

const fmodInit = System_getSymbolAddress("FMOD_System_Create");

System_getLibraryInfo(libraryId)

Returns metadata for a registered library.

const info = System_getLibraryInfo("mylib");
if (info) log("Loaded from: " + info.path);

System_getLoadedLibraries()

Returns the IDs of all currently registered libraries.

System_getLoadedLibraries().forEach(id => log("Registered: " + id));

System_getLastError()

Returns the last error string recorded by any System_* operation.

if (!System_loadLibrary("x", "/bad/path.so")) {
  logError(System_getLastError());
}

System_dlOpen(path, flags)

Raw dlopen wrapper. The returned handle is NOT tracked by the registry.

const h = System_dlOpen("/system/lib64/liblog.so");
const fn = System_dlSym(h, "__android_log_print");
System_dlClose(h);

System_dlSym(handle, symbolName)

Raw dlsym wrapper.

const logPrint = System_dlSym(liblogHandle, "__android_log_print");

System_dlClose(handle)

Raw dlclose wrapper. Returns true on success.

System_dlClose(handle);

System_dlError()

Returns and clears the last error from the dl* subsystem.

System_dlOpen("/bad/path.so");
const err = System_dlError();
if (err) logError("dlopen failed: " + err);

JS: FMOD API

FMOD.addPathOverride(originalPath, customPath)

Redirects an FMOD audio file open from originalPath to customPath.

FMOD.addPathOverride(
  "assets/music/game/creative.ogg",
  "/sdcard/MyMod/music/creative.ogg"
);

FMOD.removePathOverride(originalPath)

Removes a single path override.

FMOD.removePathOverride("assets/music/game/creative.ogg");

FMOD.clearPathOverrides()

Removes ALL registered path overrides.

FMOD.clearPathOverrides();

FMOD.pauseCurrentTrack()

Pauses the channel of the currently tracked active track.

FMOD.pauseCurrentTrack();

FMOD.resumeCurrentTrack()

Resumes the currently paused track.

FMOD.resumeCurrentTrack();

FMOD.stopCurrentTrack()

Stops only the currently tracked active track.

FMOD.stopCurrentTrack();

FMOD.stopAll()

Stops EVERY FMOD channel that is currently playing.

FMOD.stopAll();

FMOD.getCurrentTrackPath()

Returns the original (pre-override) path of the currently active track.

const path = FMOD.getCurrentTrackPath();
if (path) log("Now playing: " + path);

FMOD.isTrackPlaying()

Returns whether a track is currently in the playing (non‑paused, non‑stopped) state.

if (FMOD.isTrackPlaying()) {
  log("Audio is currently active.");
}

FMOD.setVolume(volume)

Sets the volume on the currently active FMOD channel.

FMOD.setVolume(0.25); // 25%
FMOD.setVolume(0.0);  // mute

FMOD.getVolume()

Returns the last‑known volume of the current track.

const vol = FMOD.getVolume();
log("Current volume: " + (vol * 100).toFixed(1) + "%");

C++ API Overview (nise-api)

The native API is exposed in include/nise/stub.h and is designed for Android native mods loaded from JavaScript. Core surfaces include SystemUtils, HookManager, VirtualAssets, FMODHook, plus runtime integration APIs like RenderAPI, TouchAPI, KeyAPI, and ClientLog.

Repository: https://github.com/Lodingglue/nise-api

C++: Template Workflow

1) Download the template

https://github.com/Lodingglue/nise-api/archive/refs/heads/master.zip

2) Minimal exported native function

// example_mod.h
extern "C" void StopAllMusic();

// example_mod.cpp
#include "example_mod.h"
#include "nise/stub.h"

extern "C" void StopAllMusic() {
    FMODHook::getInstance().stopAll();
}

3) Load and call from JavaScript

System.loadLibrary("my_mod", "/sdcard/MyMod/libmymod.so");
const fn = System.getSymbol("my_mod", "StopAllMusic");
if (fn) {
    fn();
}

C++: VirtualAssets

C-style API for virtual asset injection and override of APK assets via hooked AAssetManager.

VirtualAssets_BlockFile(path)

Blocks an asset path so open calls fail for that path.

VirtualAssets_AddFile(path, data, size)

Registers binary data as a virtual asset entry.

VirtualAssets_AddTextFile(path, content)

Registers UTF-8 text data as a virtual asset entry.

VirtualAssets_LoadDir(storageDir, virtualBaseDir, recursive)

Bulk-registers directory files from storage into virtual assets tree. Returns int count.

int count = VirtualAssets_LoadDir("/sdcard/MyMod/textures", "textures", true);

VirtualAssets_ReplaceFile(virtualPath, storagePath)

Maps one virtual path to a real on-disk replacement file. Returns bool.

C++: FMODHook

Audio path override and playback control over Minecraft FMOD streams.

addPathOverride(original_path, custom_path)

Redirects one in-game FMOD asset path to your custom file path.

FMODHook::getInstance().addPathOverride(
    "assets/music/game/creative.ogg",
    "/sdcard/MyMod/music/creative.ogg"
);

pauseCurrentTrack() / resumeCurrentTrack()

Pauses or resumes current track channel. Returns bool.

stopCurrentTrack() / stopAll()

Stops current track only, or all active FMOD channels. Returns bool.

getCurrentTrackPath()

Gets current original (pre-override) track path. Returns std::string.

setVolume(volume) / getVolume()

Sets or reads volume of the current track channel.

C++: RenderAPI V1.0.4

Frame render callbacks executed from the engine render thread inside the active OpenGL ES context.

RenderCallback

Type: typedef void (*RenderCallback)();

RenderAPI::Register(RenderCallback cb)

Registers a render callback to run every frame after game rendering and before buffer swap.

void DrawOverlay() {
    // issue ImGui or OpenGL draw commands here
}
RenderAPI::Register(DrawOverlay);

RenderAPI::Unregister(RenderCallback cb)

Unregisters a previously registered render callback.

RenderAPI::Unregister(DrawOverlay);

C++: TouchAPI V1.0.4

Touch event interception API for Android input events with optional event consumption.

TouchEvent Fields

FieldTypeDescription
actionintTouch action type (down, move, up…)
pointerIdintUnique ID per active touch pointer
xfloatX coordinate of the touch point
yfloatY coordinate of the touch point

TouchAPI::RegisterCallback(TouchCallback cb)

Registers a callback to receive touch events. Return true to consume/block the event.

bool OnTouch(const TouchEvent* ev) {
    if (ev->action == 0) {
        ClientLog("Input", "Touch", "ACTION_DOWN received");
    }
    return false; // let the game also receive the touch
}

TouchAPI::RegisterCallback(OnTouch);

TouchAPI::UnregisterCallback(TouchCallback cb)

Unregisters a callback from the touch pipeline.

TouchAPI::UnregisterCallback(OnTouch);

C++: KeyAPI V1.0.4

Keyboard/button event interception from the engine input pipeline.

KeyHandler

Type: typedef bool (*KeyHandler)(int keyCode, int action, int unicodeChar);

Return true to consume/block the key event.

KeyAPI::RegisterHandler(KeyHandler handler)

Registers a key handler. Handlers run in registration order.

bool OnKey(int keyCode, int action, int unicodeChar) {
    if (action == 0) {
        ClientLog("Input", "Key", "Key down event");
    }
    return false;
}

KeyAPI::RegisterHandler(OnKey);

KeyApi::UnregisterHandler(KeyHandler handler)

KeyAPI::UnregisterHandler(OnKey);

C++: ClientLog

Emits structured messages through the Apps client logger.

ClientLog(threadName, tag, message)

Signature: extern "C" void ClientLog(const char* threadName, const char* tag, const char* message);

ClientLog("ModInit", "MyMod", "Native module initialised.");
ClientLog("RenderThread", "Overlay", "Frame callback active.");

Native Dev Env Setup

Linux: Build With CMake + Android NDK

# install prerequisites
sudo pacman -S --needed git cmake ninja clang lld jdk17-openjdk unzip

# install Android NDK (example path)
mkdir -p $HOME/Android/Sdk/ndk
export ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/27.1.12297006
export CC=clang
export CXX=clang++

git clone https://github.com/Lodingglue/nise-api.git
cd nise-api

cmake -S . \
  -B build-arm64 \
  -G Ninja \
  -DCMAKE_BUILD_TYPE=Release \
  -DANDROID_ABI=arm64-v8a \
  -DANDROID_PLATFORM=android-26 \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake

cmake --build build-arm64 -j

Windows: Build With CMake + Ninja

# install: Git, CMake, Ninja, LLVM/Clang, Android NDK

set ANDROID_NDK_HOME=C:\Android\Sdk\ndk\27.1.12297006
set CC=clang
set CXX=clang++

git clone https://github.com/Lodingglue/nise-api.git
cd nise-api

cmake -S . \
  -B build-arm64 -G Ninja ^
  -DCMAKE_BUILD_TYPE=Release ^
  -DANDROID_ABI=arm64-v8a ^
  -DANDROID_PLATFORM=android-26 ^
  -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%\build\cmake\android.toolchain.cmake

cmake --build build-arm64 -j

Phone / Termux: Build In Termux + Android Toolchain

pkg update
pkg install -y git cmake ninja clang lld

# If building directly with Android toolchain files:
export ANDROID_NDK_HOME=/sdcard/Android/Sdk/ndk/27.1.12297006

git clone https://github.com/Lodingglue/nise-api.git
cd nise-api

cmake -S . \
  -B build-arm64 \
  -G Ninja \
  -DANDROID_ABI=arm64-v8a \
  -DANDROID_PLATFORM=android-26 \
  -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake

cmake --build build-arm64 -j