English | 日本語
RetroArch GLSL shader project for the GT78-VN Android handheld. The target is a restrained recreation of the original Game Boy Advance LCD rather than a CRT, modern IPS, or exaggerated pixel-grid effect.
This is a perceptual recreation, not a colorimeter-generated display profile. The default values were tuned by repeatedly comparing photographs of an original reflective GBA LCD with the result on a KONKR Pocket ADVANCE. The model is split into four independently understandable parts: pixel structure, color response, bezel shadows and an optional environmental reflection.
Download the signed KPA APK and SHA256SUMS from the
latest GitHub release.
This package is supported only on KONKR Pocket ADVANCE (Android model:
GT78-VN).
Users who only need the shader can download
GBA-Native-LCD-Shader-v0.1.1.zip from the same release or inspect the
variants/ directory.
Read the included compatibility notes before using it on another device.
RetroArch captures show the rendered output before it reaches the physical LCD:
| Shader OFF | Shader ON |
|---|---|
![]() |
![]() |
The photograph below shows the same comparison on the KONKR Pocket ADVANCE. The upper image is shader OFF; the lower image is shader ON. Unlike the digital captures, it also includes the target panel's brightness, viewing angle, surface reflection and camera response.
The following close-ups compare the finished shader on the left with a photograph of an original reflective GBA LCD on the right.
The emitting cells in the shader are intentionally a little brighter, and the color is slightly different from the photographed panel. This is caused by the moving environmental reflection band: it restores some luminance after the strongly compressed color LUT and represents reflected ambient light on the panel rather than a backlight. The reference photograph also includes its own illumination and camera response, so it is a visual target rather than a pixel-identical digital source.
The 240 x 160 GBA image maps exactly to the target's 960 x 640 panel. Each GBA pixel therefore occupies a stable 4 x 4 output block. The final optical mask uses a 4 x 8 repeating cell period: three columns carry subtle B, G and R transmission biases, while the fourth column and alternating row boundary form a dark matrix. A Gaussian attenuation shapes only the emitting face into a vertical oval without softening the fixed black matrix.
| Parameter | Default | Purpose |
|---|---|---|
MATRIX_FACE |
1.00 |
Transmission through the active cell face |
MATRIX_GAP |
0.66 |
Reduces the matrix boundary to 34% transmission |
SUBPIXEL_STRENGTH |
0.22 |
Keeps BGR separation visible but restrained |
DOT_ROUNDING |
1.00 |
Applies the accepted vertical oval attenuation to emitting cells |
SRC_BLEND |
0.04 |
Shares 4% of the source-cell color with direct neighbors |
OPTICS_CHROMA |
0.04 |
Small color crosstalk in the cover layer |
OPTICS_LUMA |
0.02 |
Smaller luminance crosstalk in the cover layer |
The dark matrix is deliberately more important than rainbow fringing. The small neighbor blends soften the overly clean edges of a modern IPS panel without using linear filtering or a general-purpose blur.
Color correction is performed before the LCD matrix with a full-strength, 256-step, per-channel LUT. It is intentionally not reducible to a single brightness, saturation or gamma value. A few representative LUT entries are:
| Input | R | G | B |
|---|---|---|---|
| 0 | 20 | 17 | 19 |
| 128 | 29 | 27 | 40 |
| 192 | 39 | 39 | 52 |
| 240 | 62 | 61 | 59 |
| 255 | 80 | 79 | 78 |
RGB_CURVE_STRENGTH is 1.00. The LUT strongly compresses the bright,
saturated output of a modern display into the muted midtones, subdued reds and
greens, and slightly cool dark tones seen in the reference photographs. The
lighting pass then restores luminance as reflected external light rather than
as an emissive backlight.
These values are calibrated for the target device and reference photographs. Different Android panels, brightness settings and vendor color management may require a different LUT.
The original GBA panel is reflective, has no internal backlight, and sits behind the front lens rather than being fully laminated to it. The shader therefore models light falling on a recessed panel instead of adding an emissive lamp curve.
With sensor lighting enabled, a face-up device starts with a 30-pixel shadow
around all four edges. Tilting the device can extend the top or bottom shadow
to 180 pixels and the left or right shadow to 80 pixels, while allowing the
opposite shadow to disappear. Its root begins at a 0.25 light multiplier and
reaches 0.10 at maximum extension. The lower panel reflection rises from
1.00 to 1.80.
Accelerometer input uses no dead zone and a 0.20 low-pass coefficient. This
keeps the shadow responsive while suppressing flicker from button presses and
small hand tremors. The distances and gains are visual tuning values for the
960 x 640 target, not measurements of the original shell in millimeters.
When sensor data is unavailable, the preset falls back to a fixed overhead
light model: rows 0-29 rise from 0.25 to 0.50, rows 30-179 recover from
0.50 to neutral light, and rows 180-639 rise from 1.00 to 1.80.
The soft horizontal reflection is not claimed to be a permanent feature of every original GBA screen. It is a stylized model of a local light source, such as a window or ceiling light, appearing on a reflective LCD surface. It has no hard rectangular boundary and moves with device tilt.
| Parameter | Default | Purpose |
|---|---|---|
BAND_LIGHT |
1.30 |
Peak gain of the broad reflection |
BAND_WIDTH |
0.46 |
Horizontal half-width of the broad band |
BAND_HEIGHT |
0.168 |
Gaussian vertical spread |
BAND_TRAVEL |
0.30 |
Maximum normalized movement with tilt |
INNER_BAND_LIGHT |
1.25 |
Absolute light level of the softer inner region |
INNER_BAND_WIDTH |
0.16 |
Inner core horizontal half-width |
INNER_BAND_HEIGHT |
0.04 |
Inner core vertical half-height |
INNER_BAND_FADE_WIDTH |
0.38 |
Horizontal fade extent |
INNER_BAND_FADE_HEIGHT |
0.14 |
Vertical fade extent |
The inner region converges to an absolute 1.25 instead of multiplying the
outer 1.30 gain. This prevents the center from becoming a clipped white bar
and gives the reflection a less uniform surface. For a stricter panel-only
presentation, set both BAND_LIGHT and INNER_BAND_LIGHT to 1.00 to disable
the reflection without changing the matrix, LUT or bezel-shadow model.
Temporal LCD response is applied separately from the four spatial components.
Brightening transitions use a rise speed of 0.81; falling transitions may
retain a diminishing trail for up to six frames. The effect is intentionally
shorter and weaker than the STN response used by the Game Gear comparison
shader.
variants/gba-reflective-v2.glslp: current six-pass release presetvariants/rgb-curve-lut.glsl: 256-step RGB color correctionvariants/gba-reflective-ghost.glsl: temporal LCD responsevariants/gba-reflective-response.glsl: source-cell optical responsevariants/gba-reflective-matrix.glsl: BGR matrix, shadows and reflectionsvariants/gba-reflective-optics.glsl: final cover-layer crosstalkvariants/reflective-column-e.glsl: optional final reflective texture passreference/device-profile.md: measured target-device configurationreference/tuning-notes.md: visual intent and parameter guidereference/photos/: user-provided photographs of an original GBA SP LCDcaptures/: before/after device captures used during tuningdocs/images/: README screenshots and physical-device comparison
For manual testing, place the files from variants/ together under:
/storage/emulated/0/RetroArch/shaders/native-lcd-v0.1.1/
Load gba-reflective-v2.glslp from RetroArch's Load Preset command. The
preset, LUT and shaders must remain in the same directory. The Android core
preset loads it automatically for mGBA. Do not save it globally until the
visual comparison and performance test are complete.
The current version does not use linear filtering or an emissive backlight. Panel softness comes from chroma diffusion with centre luminance preservation. Illumination is a directional reflected-light field with an upper bezel shadow.
The release APK includes the six-pass preset, LUT, mGBA core, menu assets and
the calibrated configuration. Managed shader files are installed into the
shared shader browser at /storage/emulated/0/RetroArch/shaders/. The default
native-lcd-v0.1.1 preset is applied automatically for mGBA, while the legacy
native-lcd-v0.1.0 preset remains selectable for comparison. Saves, states,
screenshots and playlists remain in writable shared storage.
The launcher icon uses a blue background (RGB 48, 65, 160) so this build can
be distinguished from the stock RetroArch icon. Depending on the launcher, the
app label may be shortened; the blue icon identifies RetroArch GBA LCD.
Sensor-driven lighting requires the bundled RetroArch build because it adds accelerometer uniforms to the GLSL pipeline. A standard RetroArch build can still render the preset's fixed-light fallback, but cannot move the bezel shadows or reflection with device tilt.
This APK is released specifically for the KONKR Pocket ADVANCE (Android model: GT78-VN). It is not a universal Android display profile. The rendering model assumes the KPA's 960 x 640 panel, exact 4x presentation of the 240 x 160 GBA image, measured panel color response, landscape sensor axes and AYANEO system audio.
On a different resolution, the shader still renders, but the 4 x 8 optical cell model and pixel-based shadow distances no longer represent the intended physical scale. A different panel also needs its own color LUT. Sensor axes may need to be swapped or inverted. AYANEO Equalizer support only applies to firmware that provides the compatible Android Dynamics Processing effect.
The package does not contain an artificial product-name lock, so it might launch on another Arm64 Android device. Such use is unsupported: visual accuracy, sensor direction, controls, performance and system EQ integration are not guaranteed. Other handhelds should receive a separately calibrated device profile and their own physical-device validation.
The APK is compatible with AYANEO Equalizer, the EQ control exposed by the AYASpace/AYANEO system software on the target device. This is the label shown by the device UI; AYANEO's public AYASpace page describes its system-level sound effects but does not currently publish a more specific product name for this Android EQ.
Stock RetroArch's low-latency OpenSL path bypassed the system effect. This build
requests an effect-capable OpenSL performance mode, allowing AYASpace's Android
Dynamics Processing effect to attach to the active RetroArch audio session at
48 kHz. The EQ must still be enabled and configured in AYASpace; it is not
implemented or bundled by this APK.
The effect-capable path reports a larger mixer-buffer duration than the original fast path: approximately 82 ms instead of 42 ms in AudioFlinger. This is not a measured end-to-end output delay and does not mean that every sound is always delayed by 82 ms. No perceptible difference was reported during normal play with the EQ either enabled or disabled.
The APK uses package ID com.retroarch.aarch64 and is intended for the
960 x 640 KONKR Pocket ADVANCE (Android model: GT78-VN) configuration. It
installs alongside the
stock com.retroarch package and does not reuse the stock app's configuration.
User data is stored outside the app-private managed files:
| Data | Path |
|---|---|
| Battery saves | /storage/emulated/0/RetroArch-gyrotest/saves/ |
| Save states | /storage/emulated/0/RetroArch-gyrotest/states/ |
| Screenshots | /storage/emulated/0/RetroArch-gyrotest/screenshots/ |
| Playlists | /storage/emulated/0/RetroArch-gyrotest/playlists/ |
| BIOS/system files | /storage/emulated/0/RetroArch-gyrotest/system/ |
| Main configuration | /storage/emulated/0/Android/data/com.retroarch.aarch64/files/retroarch.cfg |
In-place APK updates preserve saves, states and normal settings. Bundle version 3 performs one migration to late input polling and disables the VRR-only exact content-framerate mode; later user changes remain persistent.
Uninstalling the APK should leave the shared RetroArch-gyrotest data in
place, but Android may remove the package-specific Android/data directory and
therefore retroarch.cfg. Back up the configuration before uninstalling or
clearing application storage. Never delete RetroArch-gyrotest when it contains
saves or BIOS files that have not been backed up.
Android only accepts an in-place update when the new APK uses the same signing
key. The v0.1.0 release APK is signed with the project's persistent release
certificate documented in SIGNING_CERTIFICATE.md.
Every future version must use this same private key. Installing over an older
development-signed test build requires uninstalling it first, which can remove
the package-specific configuration described above.
Release preparation status and remaining publication work are tracked in
RELEASE_CHECKLIST.md.
Project-authored shader code, tools and documentation are released under the MIT License. The bundled APK also contains RetroArch under GPL-3.0+ and the mGBA libretro core under MPL-2.0. Their license texts and third-party notices are included inside the APK. The MIT license does not replace those component licenses.
The exact RetroArch modifications used by v0.1.1 are published in the game-de-it RetroArch fork.
This build installs the selectable presets under
/storage/emulated/0/RetroArch/shaders/. If a core-specific preset must be
restored manually, install core-presets/mGBA/gba.glslp as:
/storage/emulated/0/RetroArch/config/mGBA/gba.glslp
It references native-lcd-v0.1.1 by absolute path and applies it when mGBA
content starts. The previous core preset is kept in
backups/gba-online-updater.glslp.
tools/render_preview.py applies a CPU approximation of the current preset to
the captured no-shader image. It reads the release parameters and RGB LUT, then
creates both a rendered PNG and a three-way comparison with the no-shader
capture and original-panel photograph.
python3 tools/render_preview.py \
--preset variants/gba-reflective-v2.glslp \
--lut variants/rgb_curve_lut.pngThe default outputs are captures/preview-current.png and
captures/preview-current-comparison.png. NumPy and Pillow are required.




