Anaglyph 3d Video Player For Android Today

The Red channel is taken from the Left view; Green and Blue channels are taken from the Right view.

An idea that started as a nostalgic curiosity: bringing analog stereoscopic cinema into the palms of modern users. Anaglyph 3D—red/cyan composite images that separate views by color—was one of the earliest and most accessible ways to create a stereoscopic experience. It requires only two slightly offset images (left and right) and inexpensive complementary glasses. The project began from a simple question: could an Android app replicate the old-school anaglyph effect in real time for video playback while taking advantage of modern phone hardware, sensors, and UX expectations?

package com.example.anaglyph3d

import android.content.Intent import android.net.Uri import android.opengl.GLSurfaceView import android.os.Bundle import android.provider.OpenableColumns import android.widget.Button import androidx.appcompat.app.AppCompatActivity import android.media.MediaPlayer import android.view.Surface anaglyph 3d video player for android

class MainActivity : AppCompatActivity()

private lateinit var glSurfaceView: GLSurfaceView
private lateinit var renderer: AnaglyphRenderer
private var mediaPlayer: MediaPlayer? = null
private var videoUri: Uri? = null
private val PICK_VIDEO_REQUEST = 1
override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
glSurfaceView = findViewById(R.id.glSurfaceView)
    glSurfaceView.setEGLContextClientVersion(2)
    renderer = AnaglyphRenderer(this)
    glSurfaceView.setRenderer(renderer)
    glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
findViewById<Button>(R.id.btnPick).setOnClickListener 
        pickVideo()
findViewById<Button>(R.id.btnPlayPause).setOnClickListener 
        mediaPlayer?.let  mp ->
            if (mp.isPlaying) mp.pause() else mp.start()
private fun pickVideo() 
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply 
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "video/*"
startActivityForResult(intent, PICK_VIDEO_REQUEST)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) 
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == PICK_VIDEO_REQUEST && resultCode == RESULT_OK) 
        data?.data?.let  uri ->
            videoUri = uri
            contentResolver.takePersistableUriPermission(uri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION)
            setupMediaPlayer(uri)
private fun setupMediaPlayer(uri: Uri) 
    mediaPlayer?.release()
    mediaPlayer = MediaPlayer().apply 
        setDataSource(contentResolver.openFileDescriptor(uri, "r")?.fileDescriptor)
        prepareAsync()
        setOnPreparedListener 
            start()
            val surface = Surface(renderer.surfaceTexture)
            setSurface(surface)
            surface.release()
            glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
setOnVideoSizeChangedListener  _, width, height ->
            renderer.setVideoSize(width, height)
override fun onDestroy() 
    super.onDestroy()
    mediaPlayer?.release()
    mediaPlayer = null


@Test
public void testAnaglyphConversion() 
    Bitmap left = createSolidColorBitmap(Color.RED);
    Bitmap right = createSolidColorBitmap(Color.BLUE);
Bitmap result = anaglyphProcessor.convert(left, right);
// Red channel should come from left
int pixel = result.getPixel(0, 0);
assertEquals(Color.red(pixel), Color.red(left.getPixel(0,0)));
// Green/Blue from right
assertEquals(Color.green(pixel), Color.green(right.getPixel(0,0)));
assertEquals(Color.blue(pixel), Color.blue(right.getPixel(0,0)));


  • Optional in-app purchases: presets pack, calibration tools, curated 3D video content packs.
  • Open-source core processing library (Shader + decoder pipeline) for community contributions and portability to other platforms.
  • Share by: