Web APIs

OneJS provides browser-compatible APIs so JavaScript libraries and patterns work as expected. These are implemented using Unity's underlying systems.

localStorage

Persist data using Unity's PlayerPrefs:

// Store data
localStorage.setItem("username", "player1")
localStorage.setItem("settings", JSON.stringify({ volume: 0.8 }))

// Retrieve data
const username = localStorage.getItem("username") // "player1"
const settings = JSON.parse(localStorage.getItem("settings"))

// Remove data
localStorage.removeItem("username")

// Clear all data
localStorage.clear()

Limitations

Unlike browser localStorage:

  • localStorage.key(index) always returns null (PlayerPrefs doesn't support enumeration)
  • localStorage.length always returns 0
  • Data persists permanently (no expiration)

Example: Saving Game State

function usePersistentState(key, defaultValue) {
    const [value, setValue] = useState(() => {
        const stored = localStorage.getItem(key)
        return stored ? JSON.parse(stored) : defaultValue
    })

    useEffect(() => {
        localStorage.setItem(key, JSON.stringify(value))
    }, [key, value])

    return [value, setValue]
}

function Settings() {
    const [volume, setVolume] = usePersistentState("volume", 1.0)

    return (
        <Slider
            value={volume}
            lowValue={0}
            highValue={1}
            onChange={(e) => setVolume(e.value)}
        />
    )
}

sessionStorage

sessionStorage is an alias to localStorage in OneJS. Unity doesn't have browser-style sessions, so data persists across app restarts.

If you need session-scoped data, clear on app start:

// At app initialization
const SESSION_KEY = "__session_id"
const currentSession = Date.now().toString()
const lastSession = localStorage.getItem(SESSION_KEY)

if (lastSession !== currentSession) {
    // Clear session data from previous run
    localStorage.removeItem("temp_data")
    localStorage.setItem(SESSION_KEY, currentSession)
}

URL & URLSearchParams

Parse and manipulate URLs:

// Parse a URL
const url = new URL("https://api.example.com:8080/users?page=1&limit=10#section")

console.log(url.hostname)  // "api.example.com"
console.log(url.port)      // "8080"
console.log(url.pathname)  // "/users"
console.log(url.search)    // "?page=1&limit=10"
console.log(url.hash)      // "#section"
console.log(url.origin)    // "https://api.example.com:8080"

// Modify URL
url.pathname = "/posts"
url.searchParams.set("page", "2")
console.log(url.href)  // "https://api.example.com:8080/posts?page=2&limit=10#section"

URLSearchParams

Work with query strings:

// Create from string
const params = new URLSearchParams("foo=1&bar=2")

// Get values
params.get("foo")      // "1"
params.getAll("foo")   // ["1"]
params.has("bar")      // true

// Modify
params.set("foo", "new")
params.append("baz", "3")
params.delete("bar")

// Iterate
params.forEach((value, key) => {
    console.log(`${key}: ${value}`)
})

// Convert to string
params.toString()  // "foo=new&baz=3"

API Request Helper

function buildApiUrl(endpoint, params = {}) {
    const url = new URL(endpoint, "https://api.example.com")

    Object.entries(params).forEach(([key, value]) => {
        url.searchParams.set(key, value)
    })

    return url.href
}

// Usage
const url = buildApiUrl("/users", { page: 1, limit: 20 })
// "https://api.example.com/users?page=1&limit=20"

Base64 Encoding

Encode and decode Base64:

// Encode string to Base64
const encoded = btoa("Hello, World!")
console.log(encoded)  // "SGVsbG8sIFdvcmxkIQ=="

// Decode Base64 to string
const decoded = atob("SGVsbG8sIFdvcmxkIQ==")
console.log(decoded)  // "Hello, World!"

Use Cases

// Encode JSON for URL-safe transmission
const data = { id: 123, name: "test" }
const encoded = btoa(JSON.stringify(data))

// Basic auth header
const credentials = btoa(`${username}:${password}`)
const headers = { Authorization: `Basic ${credentials}` }

Timers

Standard JavaScript timing functions:

// One-time delay
const timeoutId = setTimeout(() => {
    console.log("Executed after 1 second")
}, 1000)

clearTimeout(timeoutId)  // Cancel

// Repeated interval
const intervalId = setInterval(() => {
    console.log("Every 500ms")
}, 500)

clearInterval(intervalId)  // Stop

// Immediate execution (microtask)
const immediateId = setImmediate(() => {
    console.log("Runs immediately after current code")
})

clearImmediate(immediateId)  // Cancel

// Animation frame (synced with Unity Update)
function animate() {
    // Update animation
    requestAnimationFrame(animate)
}
const rafId = requestAnimationFrame(animate)
cancelAnimationFrame(rafId)  // Stop

React Timer Patterns

// Interval hook
function useInterval(callback, delay) {
    const savedCallback = useRef(callback)

    useEffect(() => {
        savedCallback.current = callback
    }, [callback])

    useEffect(() => {
        if (delay === null) return

        const id = setInterval(() => savedCallback.current(), delay)
        return () => clearInterval(id)
    }, [delay])
}

// Usage
function Clock() {
    const [time, setTime] = useState(new Date())

    useInterval(() => {
        setTime(new Date())
    }, 1000)

    return <Label text={time.toLocaleTimeString()} />
}

performance.now()

High-precision timestamp for performance measurement:

const start = performance.now()

// Do work
expensiveOperation()

const end = performance.now()
console.log(`Took ${end - start}ms`)

Note: In WebGL, this is the native browser API. On other platforms, it returns the time since JavaScript started.

queueMicrotask

Schedule code to run after current task but before rendering:

queueMicrotask(() => {
    console.log("Runs after current synchronous code")
})
console.log("This logs first")

Useful for batching updates:

let pendingUpdates = []
let scheduled = false

function scheduleUpdate(update) {
    pendingUpdates.push(update)

    if (!scheduled) {
        scheduled = true
        queueMicrotask(() => {
            const updates = pendingUpdates
            pendingUpdates = []
            scheduled = false

            // Process all batched updates
            updates.forEach(u => u())
        })
    }
}

StyleSheet APIs

See Styling - StyleSheet API for runtime stylesheet management (loadStyleSheet, compileStyleSheet, removeStyleSheet, clearStyleSheets).

Platform Differences

APINative (QuickJS)WebGL (Browser)
localStoragePlayerPrefsNative localStorage
fetchUnityWebRequestNative fetch
URLPolyfillNative URL
performance.now()JS engine timeNative high-res time
setTimeoutUnity Update loopNative timers
requestAnimationFrameUnity Update loopNative RAF
The same code works on both platforms. OneJS uses native browser APIs in WebGL for better performance.