Skip to main content

What Problem Does This Solve?

Following best practices helps you:
  • Avoid common integration mistakes
  • Optimize recording performance
  • Handle errors gracefully
  • Write maintainable code
  • Deliver smooth user experience
This page collects lessons learned from hundreds of LCK integrations.

When to Use This

Read this when:
  • First-time LCK integration
  • Debugging performance issues
  • Building custom recording UI
  • Optimizing for Quest devices
  • Code review / refactoring

Recording

Quality Guidelines

Quest recommendations:
  • Quest 2: Use HD (1080p) @ 30fps for best balance
  • Quest 3/Pro: Can handle 2K if game performance allows
  • Avoid 4K on Quest devices (thermal/performance)
File size estimates:
  • SD @ 30fps (default 4 Mbps): ~2 GB per hour
  • HD @ 60fps (default 12 Mbps): ~5 GB per hour
  • 2K @ 60fps (default 20 Mbps): ~9 GB per hour
  • 4K @ 60fps (default 35 Mbps): ~16 GB per hour

Use Async Methods

Do this:
Don’t do this:
Why async is better:
  • Non-blocking—doesn’t freeze game
  • Detailed error callbacks
  • Progress tracking for save operations
  • Better user experience

Subscribe to Recording Events

Do this:
Don’t do this:
Why events are better:
  • React immediately to state changes
  • No performance cost of polling
  • Cleaner code
  • More responsive UI

Validate Before Recording


Audio

Audio Source Priority

When multiple audio plugins are enabled, LCK uses priority order:
  1. LCKFMOD (highest priority)
  2. LCKWwise
  3. LCKUnrealAudio (lowest priority, always available)
Only ONE game audio source is active at a time. Microphone and voice chat can run alongside game audio.
Check which is active:

Match Sample Rates

Do this:
Don’t do this:

Thread-Safe Audio Callbacks

Audio callbacks may come from different threads. If you need to access game thread objects (UObject properties, UI), use AsyncTask.
Do this:
Don’t do this:

Audio Delegate is Single, Not Multicast

OnAudioDataDelegate is a SINGLE delegate. Use BindLambda(), NOT AddLambda().
Do this:
Don’t do this:

UI

Don’t Add Your Own Button Cooldown

LCK buttons include automatic 0.25s cooldown. Do this:
Don’t do this:

Use Showable Groups for Batch Operations

Do this:
Don’t do this:

Performance

Optimize Scene Capture Component


Unregister Capture Components

Do this:
Don’t do this:

Monitor Frame Times During Recording


Common Pitfalls

1. Not Handling Recording State Feedback

Problem: No feedback when recording starts/stops/fails Solution: Subscribe to delegates

2. Mismatched Resolutions

Problem: Render target doesn’t match recording resolution → distortion Solution: Match exactly

3. Forgetting to Unregister Capture Component

Problem: Memory leak from orphaned capture components Solution: Always unregister on cleanup

4. Sample Rate Mismatch

Problem: Audio distortion or A/V sync issues Solution: Query and match sample rates

5. Using AddLambda for Audio Delegate

Problem: OnAudioDataDelegate is single, not multicast Solution: Use BindLambda() instead

Platform Checklist

Android (Quest)

Vulkan enabled in Project Settings → Android
RECORD_AUDIO permission in AndroidManifest.xml
WRITE_EXTERNAL_STORAGE permission (API < 29)
LCKOboe plugin enabled for low-latency mic
LCKVulkan loads at EarliestPossible (don’t change!)
AndroidManifest.xml:

Windows (PCVR)

Media Foundation available (Windows 10+)
DirectX 11 compatible GPU
H.264 hardware encoding support

Debugging

Enable Verbose Logging

What you’ll see:

Common Log Messages


Quick Reference

Start recording:
Stop recording:
Check state:
Subscribe to events:

Key Takeaways

Use async methods for better error handling
Subscribe to events instead of polling state
Validate before recording (tracking ID, storage, state)
Match sample rates to avoid audio issues
Audio callbacks on any thread — use AsyncTask for UI updates
Single delegate for audio — use BindLambda, not AddLambda
Unregister captures to prevent memory leaks
HD @ 30fps for Quest 2 — higher quality impacts performance