Skip to content
Table of contents

Artemis-Switch: A streamed frame from host to screen

Artemis-Switch is a fork in the Moonlight streaming family. The host runs the game. The client receives compressed video and audio, renders frames and sends controller input back.

Hardware and graphics Fork
  • Updated
  • 36
  • C++ · 53.78%
  • GPL-3.0
C/C++ (CMake) JVM (Gradle) Upstream credited
Documented components
2Explore modules
Source documents
6Read the instructions
Repository languagesC++ 53.78%11

Shares calculated from GitHub Linguist bytes.

C++53.78%
53.78%2,312,036 bytes
C33.23%
33.23%1,428,795 bytes
Java5.69%
5.69%244,691 bytes
CMake3.51%
3.51%150,826 bytes
Language%Bytes
C++53.78%2,312,036
C33.23%1,428,795
Java5.69%244,691
CMake3.51%150,826
Objective-C++1.95%83,846
Shell0.75%32,086
5 more languages
Swift0.38%16,275
Python0.35%15,226
GLSL0.34%14,440
Makefile0.02%662
PowerShell0.01%271

1. Overview

1.1. Why it exists

Hardware-accelerated GameStream/Moonlight client in C++ with custom NVN display frame pacing for zero-stutter video.

1.2. Best fit

Ultra-low latency PC game streaming directly to Nintendo Switch handheld mode.

2. Use cases

Moonlight-Switch fork that streams PC games to a Nintendo Switch, with FSR upscaling, stream profiles and telemetry.

Use-case diagram for Artemis-Switch: Pair and stream from a host; Tune video presentation; Save stream profiles; Export a benchmark; Build and test the appArtemis-SwitchPair and stream from a hostTune video presentationSave stream profilesExport a benchmarkBuild and test the appSwitch playerDeveloper«system»GameStream host PC
People on the left, external systems on the right; each oval is a use case backed by the files cited below.
Pair and stream from a host
Switch playerGameStream host PC

GameStreamClient.cpp and MoonlightSession.cpp handle PIN pairing and sessions with Sunshine, Apollo, Vibeshine or Punktfunk; DiscoverManager finds hosts and WakeOnLanManager can wake them.

Tune video presentation
Switch player

Fit, Fill, Stretch, Zoom/Pan, rotation and full range share one deko3d presentation path, with FSR/EASU upscaling, RCAS sharpening and dithering on top of NVDEC decode.

Save stream profiles
Switch playerGameStream host PC

Named profiles store FPS (30 to 120), resolution, bitrate, codec and presentation in profile.json; they can be assigned per host, edited and imported or exported (StreamProfileStore.cpp, ProfileEditorDialog.cpp).

Export a benchmark
Switch player

The Performance page shows bitrate, latency, frame queue, GPU time and Switch clocks; Benchmark start, stop and save write a file through app/src/benchmark/BenchmarkExport.cpp.

Build and test the app
Developer

With devkitA64 and CMake, the Moonlight.nro target builds the Switch binary, exported by CI as Artemis-Switch.nro; tests/ holds portable unit tests run with ctest, plus Python i18n and release checks.

2.1. Scenario: Stream a PC game to a Switch

  1. Copy Artemis-Switch.nro to sdmc:/switch/Artemis-Switch/ and start hbmenu through title redirection.
  2. Open Artemis Switch, select the PC host and finish PIN pairing in the host’s web UI.
  3. Create a stream profile (for example 60 FPS, H.265, Fit with FSR) and assign it to that host.
  4. Launch an app and press - and + together to open the in-stream overlay.
  5. Open Performance to watch bitrate and pipeline latency, then run and save a Benchmark.

Files readREADME.mdapp/src/streaming/GameStreamClient.cppapp/src/streaming/MoonlightSession.cppapp/src/streaming/StreamProfileStore.cppapp/src/benchmark/BenchmarkExport.cpptests/CMakeLists.txtCMakeLists.txt

3. Getting started

These commands come from the repository documentation. They were not run during this review.

README.md · Developer build
git clone --recursive https://github.com/antoinebou12/Artemis-Switch.git
cd Artemis-Switch
cmake -B build/switch -DCMAKE_BUILD_TYPE=Release -DPLATFORM_SWITCH=ON -DUSE_DEKO3D=ON
cmake --build build/switch --target Moonlight.nro --parallel
README.md · Developer validation
python tests/i18n_consistency_test.py
python tests/release_contract_test.py
cmake -S tests -B build/tests -DCMAKE_BUILD_TYPE=Debug
cmake --build build/tests --parallel
ctest --test-dir build/tests --output-on-failure
README.md · Developer validation
python -m pip install ruff==0.12.10
python scripts/lint.py
cmake -S tests -B build/lint -DCMAKE_BUILD_TYPE=Debug -DARTEMIS_STRICT_WARNINGS=ON -DARTEMIS_ENABLE_CLANG_TIDY=ON
cmake --build build/lint --parallel
README.md · Developer validation
cmake -S ci/integration -B build/integration -DCMAKE_BUILD_TYPE=Debug
cmake --build build/integration --parallel
ctest --test-dir build/integration --output-on-failure
app/platforms/android-project/README.md · Building
cd android-project
app/platforms/android-project/README.md · Building
./build_libromfs_generator.sh
app/platforms/android-project/README.md · Building
export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home
export ANDROID_SDK_ROOT=~/Library/Android/sdk
./gradlew assembleDebug
./gradlew installDebug

4. Repository references

Excerpts from the repository’s guides, in their original language. Links open the inspected source revision.

Prerequisites and first run

4.1. Install the Artemis Switch NRO

docs/NRO_INSTALL.md

Use one of these files from dist/nro:

  1. Easy install: extract Artemis-Switch-SD.zip to the root of the Switch SD card.
  2. Manual install: copy Artemis-Switch.nro to sdmc:/switch/Artemis-Switch/Artemis-Switch.nro. Launch the Homebrew Menu with title redirection/full RAM mode, then select Artemis Switch. Applet mode is not supported for reliable streaming. The ZIP contains only the ready-to-copy switch/Artemis-Switch/Artemis-Switch.nro layout. Apollo and Sunshine both use the normal Moonlight/GameStream pairing flow.
Read the full instructions ↗

4.2. Install

docs/source/install.md

Use a file from a GitHub Release or from dist/nro after a local Switch build.
Read the full instructions ↗

4.3. Easy install

docs/source/install.md

Extract Artemis-Switch-SD.zip to the root of the Switch SD card. That ZIP is only the ready-to-copy layout:
Read the full instructions ↗

4.4. Manual install

docs/source/install.md

Copy Artemis-Switch.nro to:
Read the full instructions ↗
Configuration and usage

4.5. Recommended host and clients

README.md

Hosts and clients are separate. Pick a server on the PC, then a client on each device. Artemis uses Moonlight/GameStream for pairing and streaming; host administration stays in the selected host’s own web UI.
Read the full instructions ↗

4.6. Client (device)

README.md

ClientDeviceNotes
Artemis Switch (this app)Nintendo SwitchMoonlight-Switch fork with FSR/RCAS, stream profiles, and Switch presentation.
ArtemideAndroidHighly recommended on Android: ultra-low latency (ULL), direct presentation, built-in FSR upscaling. FSR Performance / Balanced / Quality presets inspired this fork.
Artemis (Moonlight Noir)AndroidApollo’s companion client (virtual display / host UX inspiration).
MoonlightPC, Android, iOS, …Official clients.
Moonlight V+AndroidAlternative Android fork (frame-gen and extra host tools).
Read the full instructions ↗
Development and contributions

4.7. Developer build

README.md

Prerequisites: Git, CMake 3.20 or newer, and a standard devkitPro devkitA64 environment. Clone recursively so every dependency under extern/ is initialized: Repository layout

PathPurpose
app/src/Application, streaming, renderer, input, and feature implementation
app/include/UI and application declarations
resources/Borealis XML, images, shaders, and translations
tests/Portable unit and contract tests
ci/integration/Cross-feature integration test build
docs/GitHub Pages source and engineering notes
.github/workflows/Build, test, sanitizer, and release automation
extern/Pinned third-party dependencies and Git submodules
Keep Switch-specific behavior behind the existing platform and capability gates.
Do not make project-specific changes inside vendored extern/ dependencies.
Read the full instructions ↗

4.8. Developer validation

README.md

Lint changed first-party files and run the strict portable C++ analysis surface: The changed-file linter checks UTF-8/text hygiene, JSON and Python syntax, Ruff, and clang-format. Vendored dependencies under extern/ are excluded. Cross-feature integration:
Read the full instructions ↗
Source documentation

6 documents read from 6 candidates.

5. Features and components 2

Session and interface
The Borealis interface handles host selection and settings. MoonlightSession coordinates streaming, while the Switch decoder-and-render provider chooses concrete video and audio implementations. UI state and media processing have different lifecycles.
Platform-specific output
The provider constructs FFmpegVideoDecoder. With BOREALIS_USE_DEKO3D it selects ArtemisDKVideoRenderer, while other build flags select other renderers. Switch audio uses AudrenAudioRenderer. These are explicit build-time choices.

6. Architecture

Expand a component to follow its connections, or explore the full diagram.

System components Context
Mermaid Interactive diagram

System overview of the components and connections verified in the repository.

Static architecture view for Artemis-Switch
Static preview. The interactive diagram replaces it after Mermaid loads.
flowchart LR
  n0["GameStream host"]
  n1["Streaming session"]
  n2["FFmpeg video decoder"]
  n3["deko3d renderer"]
  n4["Switch display"]
  n5["Controller input"]
  n0 --> n1
  n1 --> n2
  n2 --> n3
  n3 --> n4
  n5 --> n1
  n1 --> n0
Text version of the diagram
  • GameStream host → Streaming session
  • Streaming session → FFmpeg video decoder
  • FFmpeg video decoder → deko3d renderer
  • deko3d renderer → Switch display
  • Controller input → Streaming session
  • Streaming session → GameStream host
Follow one path through the code

Read the provider methods to see which backend is actually compiled, then follow the session’s decoded frames into that renderer. Network, decode and presentation delays need separate measurements to explain a slow stream.

7. Technical dossier

Built from the source at commit 33e6854 (634 files). Each item comes from a pattern match in the code, so open the linked file before relying on it.

7.1. Layers and modules

Folders are sorted into layers by name, from the user interface down to data access. Because the sorting uses names only, the diagram says nothing about calls between layers.

Mermaid Layered view
flowchart TB
  subgraph L_presentation["Presentation"]
    direction TB
    presentation_0["resources/xml<br/>19 files"]
    presentation_1["features/ui<br/>9 files"]
    presentation_2["platforms/android-project<br/>5 files"]
    presentation_3["views<br/>2 files"]
  end
  subgraph L_application["Application services"]
    direction TB
    application_0["src<br/>51 files"]
    application_1["remote_access/tailscale<br/>41 files"]
    application_2["streaming<br/>37 files"]
    application_3["include<br/>27 files"]
    application_more["+28"]
  end
  subgraph L_infrastructure["Data and infrastructure"]
    direction TB
    infrastructure_0["switch/libavutil<br/>100 files"]
    infrastructure_1["switch/libavcodec<br/>25 files"]
    infrastructure_2["switch/curl<br/>12 files"]
    infrastructure_3["remote_access/providers<br/>6 files"]
    infrastructure_more["+2"]
  end
  subgraph L_core["Core source"]
    direction TB
    core_0["(root)<br/>1 file"]
    core_1["docs/source<br/>1 file"]
  end
  subgraph L_tests["Tests"]
    direction TB
    tests_0["tests<br/>78 files"]
  end
  subgraph L_hosting["Build, CI and hosting"]
    direction TB
    hosting_0[".github<br/>15 files"]
    hosting_1["(root)<br/>2 files"]
    hosting_2["platforms<br/>2 files"]
    hosting_3["ci<br/>1 file"]
    hosting_more["+2"]
  end
  L_presentation ~~~ L_application
  L_application ~~~ L_infrastructure
  L_infrastructure ~~~ L_core
  L_core ~~~ L_tests
  L_core ~~~ L_hosting
Modules and their main layer
ModuleLayerFilesTypesExtensions
switch/libavutilData and infrastructure100195.h
srcApplication services5139.cpp
remote_access/tailscaleApplication services4162.cpp .hpp .h
streamingApplication services3732.hpp .cpp
includeApplication services2730.hpp
switch/libavcodecData and infrastructure2530.h
platforms/android-projectApplication services2435.java .xml .gradle
streaming/videoApplication services2416.hpp .glsl .cpp
features/inputApplication services2122.hpp .cpp
resources/xmlPresentation190.xml
remote_accessApplication services1720.hpp .cpp
features/videoApplication services138.hpp .cpp
switch/curlData and infrastructure1218.h
benchmarkApplication services1113.hpp .cpp

29 smaller modules are not shown.

7.2. C4 model

From system context to code declarations. Each level reflects the available source evidence.

UML 1 · System context
1 · System context

The widest C4 view: who uses the project and which outside systems and data stores it relies on. Rendered with uml-mcp.

PlantUML source
@startuml
!include <C4/C4_Context>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System(SYS, "Artemis-Switch", "Artemis-Switch is a fork in the Moonlight streaming family")
SystemDb(D0, "Active Record")
Rel(P_cli, SYS, "Uses")
Rel(SYS, D0, "Reads and writes")
@enduml
UML 2 · Containers
2 · Containers

One level closer: the parts that run separately inside the project, their main language and how calls flow between them. Rendered with uml-mcp.

PlantUML source
@startuml
!include <C4/C4_Container>
LAYOUT_LEFT_RIGHT()
Person(P_cli, "CLI user")
System_Boundary(SYS, "Artemis-Switch") {
  Container(C0, "artemis-switch-build", "Docker")
  ContainerDb(DB0, "Active Record", "")
}
Rel(P_cli, C0, "Uses")
Rel(C0, DB0, "Reads and writes")
@enduml
UML 3 · Components and layers
3 · Components and layers

Modules grouped by source-layer classification. Boundaries show organization, not runtime isolation or call relationships. Rendered with uml-mcp.

PlantUML source
@startuml
!include <C4/C4_Component>
LAYOUT_TOP_DOWN()
System_Boundary(SYS, "Artemis-Switch") {
Boundary(L0, "Presentation", "layer") {
Component(M0_0, "resources/xml", ".xml", "19 files")
Component(M0_1, "features/ui", "C++", "9 files")
Component(M0_2, "platforms/android-project", ".xml", "5 files")
Component(M0_3, "views", "C++", "2 files")
Component(M0_4, "docs/_static", ".css", "1 files")
}
Boundary(L1, "Application services", "layer") {
Component(M1_0, "src", "C++", "51 files")
Component(M1_1, "remote_access/tailscale", "C++, C/C++", "41 files")
Component(M1_2, "streaming", "C++", "37 files")
Component(M1_3, "include", "C++", "27 files")
Component(M1_4, "streaming/video", "C++, .glsl", "24 files")
Component(M1_5, "features/input", "C++", "21 files")
Component(M1_6, "platforms/android-project", "Java, .gradle, .xml", "19 files")
Component(M1_7, "remote_access", "C++", "17 files")
Component(M1_more, "+24 modules", "", "")
}
Boundary(L2, "Data and infrastructure", "layer") {
Component(M2_0, "switch/libavutil", "C/C++", "100 files")
Component(M2_1, "switch/libavcodec", "C/C++", "25 files")
Component(M2_2, "switch/curl", "C/C++", "12 files")
Component(M2_3, "remote_access/providers", "C++", "6 files")
Component(M2_4, "switch/libavformat", "C/C++", "4 files")
Component(M2_5, "switch/libswresample", "C/C++", "3 files")
}
Boundary(L3, "Core source", "layer") {
Component(M3_0, "(root)", ".sh", "1 files")
Component(M3_1, "docs/source", "Python", "1 files")
}
Boundary(L4, "Tests", "layer") {
Component(M4_0, "tests", "C++, Python", "78 files")
}
Boundary(L5, "Build, CI and hosting", "layer") {
Component(M5_0, ".github", ".yml, .md", "15 files")
Component(M5_1, "(root)", ".txt, .yml", "2 files")
Component(M5_2, "platforms", ".txt", "2 files")
Component(M5_3, "ci", ".txt", "1 files")
Component(M5_4, "docs", "Makefile", "1 files")
Component(M5_5, "extern", ".txt", "1 files")
}
}
@enduml
UML 4 · Code
4 · Code

Selected declarations, fields and methods grouped by source file. No inheritance or call edges are inferred. Rendered with uml-mcp.

PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
top to bottom direction
hide empty members
package "Artemis-Switch" {
package "app/src/utils/Settings.hpp" as F0 {
class "Settings" as T0_0 {
  working_dir()
  set_working_dir()
  launch_path()
  set_launch_path()
  key_dir()
  boxart_dir()
}
}
package "app/platforms/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java" as F1 {
class "SDLActivity" as T1_0 {
  getMotionListener()
  main()
  getMainSharedObject()
  getMainFunction()
  getLibraries()
  loadLibraries()
}
}
package "app/src/remote_access/tailscale/TailscaleWgxRoute.cpp" as F2 {
class "RealWgxBackend" as T2_0 {
  realEgressCallback()
  sendEgress()
  toSockaddr()
  fromSockaddr()
  discoverLanAddress()
  startDirectPath()
}
}
package "app/src/streaming/MoonlightSession.hpp" as F3 {
class "MoonlightSession" as T3_0 {
  set_provider()
  start()
  stop()
  stopForApplicationExit()
  set_address()
  setApolloLaunchOptions()
}
}
package "app/src/streaming/AVFrameHolder.hpp" as F4 {
class "AVFrameQueue" as T4_0 {
  push()
  pushTransferred()
  recycleWriteFrame()
  configure()
  capacityFor()
  size()
}
class "AVFrameHolder" as T4_1 {
  push()
  pushTransferred()
  get()
  acquireWriteFrame()
  recycleWriteFrame()
  prepare()
}
}
package "app/src/streaming/FramePipelineTelemetry.hpp" as F5 {
class "FramePipelineTelemetry" as T5_0 {
  reset()
  noteStreamStart()
  noteFirstPacket()
  noteDecodedFrame()
  consumePendingDecodeTiming()
  noteFrameQueued()
}
}
package "app/src/remote_access/tailscale/TailscaleCore.hpp" as F6 {
class "TailscaleCore" as T6_0 {
  start()
  stop()
  snapshot()
  resolveRoute()
  activateRoute()
  prepareRouteForStreaming()
}
}
package "app/platforms/android-project/app/src/main/java/org/libsdl/app/SDLSurface.java" as F7 {
class "SDLSurface" as T7_0 {
  handlePause()
  handleResume()
  getNativeSurface()
  enableMediaCodecOverlay()
  surfaceCreated()
  surfaceDestroyed()
}
}
package "app/src/remote_access/tailscale/relay/TsLwipRelay.hpp" as F8 {
class "TsLwipRelay" as T8_0 {
  start()
  stop()
  addTcp()
  addUdp()
  isRunning()
  log()
}
}
package "lib/switch/include/libavcodec/codec.h" as F9 {
class "AVCodec" as T9_0 {
  name
  long_name
  type
  id
  capabilities
  max_lowres
  supported_framerates
  pix_fmts
}
}
package "lib/switch/include/libavcodec/avcodec.h" as F10 {
class "AVCodecContext" as T10_0 {
  av_class
  log_level_offset
  codec_type
  codec
  codec_id
  codec_tag
  priv_data
  internal
}
class "AVCodecParserContext" as T10_1 {
  priv_data
  parser
  frame_offset
  cur_offset
  next_frame_offset
  pict_type
  repeat_pict
  pts
}
}
package "lib/switch/include/libavcodec/codec_desc.h" as F11 {
class "AVCodecDescriptor" as T11_0 {
  id
  type
  name
  long_name
  props
  NULL
  mime_types
  profiles
}
}
package "lib/switch/include/libavutil/dovi_meta.h" as F12 {
class "AVDOVIColorMetadata" as T12_0 {
  dm_metadata_id
  scene_refresh_flag
  ycc_to_rgb_matrix
  ycc_to_rgb_offset
  rgb_to_lms_matrix
  signal_eotf
  signal_eotf_param0
  signal_eotf_param1
}
class "AVDOVIDataMapping" as T12_1 {
  vdr_rpu_id
  mapping_color_space
  mapping_chroma_format_idc
  curves
  nlq_method_idc
  num_x_partitions
  num_y_partitions
  nlq
}
class "AVDOVIDecoderConfigurationRecord" as T12_2 {
  dv_version_major
  dv_version_minor
  dv_profile
  dv_level
  rpu_present_flag
  el_present_flag
  bl_present_flag
  dv_bl_signal_compatibility_id
}
class "AVDOVIDmData" as T12_3 {
  level
  l1
  l2
  l3
  l4
  l5
  l6
  l8
}
class "AVDOVIDmLevel1" as T12_4 {
  min_pq
  max_pq
  avg_pq
  target_max_pq
  trim_slope
  trim_offset
  trim_power
  trim_chroma_weight
}
class "AVDOVIDmLevel10" as T12_5 {
  target_display_index
  target_max_pq
  target_min_pq
  target_primary_index
  target_display_primaries
  content_type
  whitepoint
  reference_mode_flag
}
class "AVDOVIDmLevel11" as T12_6 {
  content_type
  whitepoint
  reference_mode_flag
  sharpness
  noise_reduction
  mpeg_noise_reduction
  frame_rate_conversion
  brightness
}
class "AVDOVIDmLevel2" as T12_7 {
  target_max_pq
  trim_slope
  trim_offset
  trim_power
  trim_chroma_weight
  trim_saturation_gain
  ms_weight
  min_pq_offset
}
class "AVDOVIDmLevel254" as T12_8 {
  dm_mode
  dm_version_index
  dm_run_mode
  dm_run_version
  dm_debug
  level
  l1
  l2
}
class "AVDOVIDmLevel255" as T12_9 {
  dm_run_mode
  dm_run_version
  dm_debug
  level
  l1
  l2
  l3
  l4
}
}
}
@enduml

7.3. Public interface

Outside code can use everything listed here. The project also declares 637 public types.

Command line 2

Entry points

Public headers 175

app/include/about_tab.hpp app/include/active_remote_route.hpp app/include/add_host_tab.hpp app/include/app_cell.hpp app/include/app_list_view.hpp app/include/artemis_settings_tab.hpp app/include/button_selecting_dialog.hpp app/include/discovery_candidate.hpp app/include/favorite_tab.hpp app/include/forwarder_maker.hpp

7.4. Private interface

These names are internal by the language’s conventions: a leading underscore, private, static or pub(crate).

anonymous namespace
98
private / internal method
65
static (file-local) function
35

7.5. Services

Service 25

7.6. Entities and data model 436

Mermaid Entity class diagram
classDiagram
  direction LR
  class E0["AVCodec"] {
    +char name
    +char long_name
    +enum type
    +enum id
    +int capabilities
    +uint8_t max_lowres
  }
  class E1["AVCodecContext"] {
    +AVClass av_class
    +int log_level_offset
    +enum codec_type
    +struct codec
    +enum codec_id
    +unsigned codec_tag
  }
  class E2["AVCodecDescriptor"] {
    +enum id
    +enum type
    +char name
    +char long_name
    +int props
    +NULL
  }
  class E3["AVCodecParserContext"] {
    +void priv_data
    +struct parser
    +int64_t frame_offset
    +int64_t cur_offset
    +int64_t next_frame_offset
    +int pict_type
  }
  class E4["AVDOVIColorMetadata"] {
    +uint8_t dm_metadata_id
    +uint8_t scene_refresh_flag
    +AVRational ycc_to_rgb_matrix
    +AVRational ycc_to_rgb_offset
    +AVRational rgb_to_lms_matrix
    +uint16_t signal_eotf
  }
  class E5["AVDOVIDataMapping"] {
    +uint8_t vdr_rpu_id
    +uint8_t mapping_color_space
    +uint8_t mapping_chroma_format_idc
    +AVDOVIReshapingCurve curves
    +enum nlq_method_idc
    +uint32_t num_x_partitions
  }
  class E6["AVDOVIDecoderConfigurationRecord"] {
    +uint8_t dv_version_major
    +uint8_t dv_version_minor
    +uint8_t dv_profile
    +uint8_t dv_level
    +uint8_t rpu_present_flag
    +uint8_t el_present_flag
  }
  class E7["AVDOVIDmData"] {
    +uint8_t level
    +AVDOVIDmLevel1 l1
    +AVDOVIDmLevel2 l2
    +AVDOVIDmLevel3 l3
    +AVDOVIDmLevel4 l4
    +AVDOVIDmLevel5 l5
  }
  class E8["AVDOVIDmLevel1"] {
    +uint16_t min_pq
    +uint16_t max_pq
    +uint16_t avg_pq
    +uint16_t target_max_pq
    +uint16_t trim_slope
    +uint16_t trim_offset
  }
  class E9["AVDOVIDmLevel10"] {
    +uint8_t target_display_index
    +uint16_t target_max_pq
    +uint16_t target_min_pq
    +uint8_t target_primary_index
    +AVColorPrimariesDesc target_display_primaries
    +uint8_t content_type
  }
  E1 --> E0 : codec
  E7 --> E8 : l1

An arrow links a field to the entity its type names.

EntityKindFirst fields
AVCodecData structname, long_name, type, id, capabilities, max_lowres, supported_framerates, pix_fmts
AVCodecContextData structav_class, log_level_offset, codec_type, codec, codec_id, codec_tag, priv_data, internal
AVCodecDescriptorData structid, type, name, long_name, props, NULL, mime_types, profiles
AVCodecParserContextData structpriv_data, parser, frame_offset, cur_offset, next_frame_offset, pict_type, repeat_pict, pts
AVDOVIColorMetadataData structdm_metadata_id, scene_refresh_flag, ycc_to_rgb_matrix, ycc_to_rgb_offset, rgb_to_lms_matrix, signal_eotf, signal_eotf_param0, signal_eotf_param1
AVDOVIDataMappingData structvdr_rpu_id, mapping_color_space, mapping_chroma_format_idc, curves, nlq_method_idc, num_x_partitions, num_y_partitions, nlq
AVDOVIDecoderConfigurationRecordData structdv_version_major, dv_version_minor, dv_profile, dv_level, rpu_present_flag, el_present_flag, bl_present_flag, dv_bl_signal_compatibility_id
AVDOVIDmDataData structlevel, l1, l2, l3, l4, l5, l6, l8
Show 6 more entities
AVDOVIDmLevel1Data structmin_pq, max_pq, avg_pq, target_max_pq, trim_slope, trim_offset, trim_power, trim_chroma_weight
AVDOVIDmLevel10Data structtarget_display_index, target_max_pq, target_min_pq, target_primary_index, target_display_primaries, content_type, whitepoint, reference_mode_flag
AVDOVIDmLevel11Data structcontent_type, whitepoint, reference_mode_flag, sharpness, noise_reduction, mpeg_noise_reduction, frame_rate_conversion, brightness
AVDOVIDmLevel2Data structtarget_max_pq, trim_slope, trim_offset, trim_power, trim_chroma_weight, trim_saturation_gain, ms_weight, min_pq_offset
AVDOVIDmLevel254Data structdm_mode, dm_version_index, dm_run_mode, dm_run_version, dm_debug, level, l1, l2
AVDOVIDmLevel255Data structdm_run_mode, dm_run_version, dm_debug, level, l1, l2, l3, l4

7.7. Business logic

Once UI code and the services and entities above are set aside, these classes have the most methods.

Settings Application services, 157 methods
working_dir, set_working_dir, launch_path, set_launch_path, key_dir, boxart_dir
SDLActivity Application services, 116 methods
getMotionListener, main, getMainSharedObject, getMainFunction, getLibraries, loadLibraries
RealWgxBackend Application services, 37 methods
realEgressCallback, sendEgress, toSockaddr, fromSockaddr, discoverLanAddress, startDirectPath
MoonlightSession Application services, 35 methods
set_provider, start, stop, stopForApplicationExit, set_address, setApolloLaunchOptions
AVFrameQueue Application services, 28 methods
push, pushTransferred, recycleWriteFrame, configure, capacityFor, size
AVFrameHolder Application services, 23 methods
push, pushTransferred, get, acquireWriteFrame, recycleWriteFrame, prepare
FramePipelineTelemetry Application services, 16 methods
reset, noteStreamStart, noteFirstPacket, noteDecodedFrame, consumePendingDecodeTiming, noteFrameQueued
TailscaleCore Application services, 21 methods
start, stop, snapshot, resolveRoute, activateRoute, prepareRouteForStreaming
SDLSurface Application services, 20 methods
handlePause, handleResume, getNativeSurface, enableMediaCodecOverlay, surfaceCreated, surfaceDestroyed
TsLwipRelay Application services, 20 methods
start, stop, addTcp, addUdp, isRunning, log

7.8. Database and persistence

7.9. External integrations

HTTP clients

libcurl

Hosts named in the code

7.10. Stack and dependencies

C/C++ (CMake) CMakeLists.txt 8 runtime

CMake ≥ 3.10

Open the manifest

JVM (Gradle) app/platforms/android-project/app/build.gradle

compileSdk Version minSdk Version targetSdk Version

Open the manifest

External dependencies

8 declared packages (8 runtime, 0 development) in CMakeLists.txt, app/platforms/android-project/app/build.gradle.

Package table 8
External dependencies
PackageVersionScopePurposeRegistry
PkgConfig—runtime—C/C++ (CMake)
MbedTLS—runtime—C/C++ (CMake)
OpenSSL—runtime—C/C++ (CMake)
SDL2—runtimeGraphics, imaging and 3DC/C++ (CMake)
CURL—runtime—C/C++ (CMake)
Jansson—runtime—C/C++ (CMake)
EXPAT—runtime—C/C++ (CMake)
PNG—runtime—C/C++ (CMake)

7.11. CI/CD pipeline

14 GitHub Actions workflows, 27 jobs. Each box is a job; double-bordered boxes publish or deploy, and arrows follow needs.

Mermaid Pipeline flow
flowchart LR
  subgraph wf0["All Builds"]
    direction LR
    wf0_switch["Nintendo Switch<br/>reuses docker-image.yml"]
    wf0_psvita["PS Vita<br/>reuses psvita.yml"]
    wf0_android["Android<br/>reuses android-apk.yml"]
    wf0_macos["macOS<br/>reuses macos.yml"]
    wf0_windows["Windows<br/>reuses windows.yml"]
    wf0_linux["Linux<br/>reuses linux.yml"]
  end
  subgraph wf1["Android APK"]
    direction LR
    wf1_build["build<br/>ubuntu-latest"]
  end
  subgraph wf2["Build Artemis Switch NRO into dist (one-sho…"]
    direction LR
    wf2_build_and_commit[["build-and-commit<br/>ubuntu-latest"]]
  end
  subgraph wf3["Build Artemis Switch"]
    direction LR
    wf3_build[["build<br/>ubuntu-latest"]]
  end
  subgraph wf4["Docs"]
    direction LR
    wf4_deploy[["deploy<br/>ubuntu-latest"]]
  end
  subgraph wf5["Feature and Integration CI"]
    direction LR
    wf5_portable_integration[["Consolidated portable integration<br/>ubuntu-latest"]]
    wf5_release_package_contract["Release source package contract<br/>ubuntu-latest"]
    wf5_sanitizer_integration["ASan + UBSan integration<br/>ubuntu-latest"]
    wf5_ci_summary["Artemis CI summary<br/>ubuntu-latest"]
  end
  t0(["This · workflow · fans · out · to · six"])
  t1(["called by a workflow"])
  t2(["push"])
  t3(["manual run · called by a workflow"])
  t4(["push · manual run"])
  t5(["Topic · branches · run · through · pull req…"])
  d0[("GitHub Pages")]
  t0 --> wf0_switch
  t0 --> wf0_psvita
  t0 --> wf0_android
  t0 --> wf0_macos
  t0 --> wf0_windows
  t0 --> wf0_linux
  t1 --> wf1_build
  t2 --> wf2_build_and_commit
  t3 --> wf3_build
  t4 --> wf4_deploy
  wf4_deploy -.-> d0
  t5 --> wf5_portable_integration
  t5 --> wf5_release_package_contract
  t5 --> wf5_sanitizer_integration
  wf5_portable_integration --> wf5_ci_summary
  wf5_release_package_contract --> wf5_ci_summary
  wf5_sanitizer_integration --> wf5_ci_summary
Workflows, triggers, jobs and targets
WorkflowTriggersJobsPublishes to
All Builds
.github/workflows/all-builds.yml
This, workflow, fans, out, to, six
  • Nintendo Switch
  • PS Vita
  • Android
  • macOS
  • Windows
  • Linux
—
Android APK
.github/workflows/android-apk.yml
called by a workflow
  • build ubuntu-latest
    Check out repository · Fetch vcpkg history and baseline · Set up Java · Set up Android SDK · Install Android SDK components · Install native build tools
—
Build Artemis Switch NRO into dist (one-shot)
.github/workflows/build-dist-once.yml
push
  • build-and-commit ubuntu-latest
    Check out repository · Build Nintendo Switch NRO and capture diagnostics · Publish NRO or failure log to main
—
Build Artemis Switch
.github/workflows/docker-image.yml
manual run, called by a workflow
  • build ubuntu-latest
    Check out repository · Remove bundled fonts · Remove conflicting enet package · Configure (compatibility pass) · Configure · Build Nintendo Switch NRO
—
Docs
.github/workflows/docs.yml
push, manual run
  • deploy ubuntu-latest
    actions/checkout · actions/setup-python · Install docs dependencies · Build HTML · Add .nojekyll · Deploy to GitHub Pages
GitHub Pages
Feature and Integration CI
.github/workflows/feature-integration-ci.yml
Topic, branches, run, through, pull request, Keeping
  • Consolidated portable integration ubuntu-latest
    Checkout · Validate localization JSON syntax · Validate Artemis English/French translation coverage · Validate release/CD contract · Validate isolated VPN build contract · Configure unit suite
  • Release source package contract ubuntu-latest
    Checkout repository and submodules · Build dry-run source packages · Inspect source packages
  • ASan + UBSan integration ubuntu-latest
    Checkout · Configure · Build · Test
  • Artemis CI summary ← portable-integration, release-package-contract, sanitizer-integration ubuntu-latest
    Require all integration jobs
—
Lint
.github/workflows/lint.yml
Topic, branches, are, linted, through, pull request
  • Changed-file lint ubuntu-latest
    Checkout full history · Install pinned linters · Select comparison revision · Lint changed first-party files · Lint complete first-party Python surface
  • Portable warnings and clang-tidy ubuntu-latest
    actions/checkout · Install clang-tidy · Configure strict portable build · Build lint surface
—
Linux Builds
.github/workflows/linux.yml
called by a workflow, manual run
  • build matrix
    Portable x86_64 · Portable ARM64 · Steam Runtime 4 x86_64 · Steam Runtime 4 ARM64 · Bootstrap checkout dependencies · Check out repository
—

6 more workflows are not shown.

7.12. Deployment

Mermaid Deployment diagram
flowchart LR
  repo[("GitHub repository")]
  ci[["GitHub Actions<br/>14 workflows"]]
  pub_GitHub_Pages[("GitHub Pages")]
  pub_GitHub_Releases[("GitHub Releases")]
  subgraph compose["Docker Compose"]
    svc_artemis_switch_build["artemis-switch-build"]
  end
  repo -->|"push or pull request"| ci
  ci -->|"publishes"| pub_GitHub_Pages
  ci -->|"publishes"| pub_GitHub_Releases
  repo -->|"docker compose up"| compose
UML Deployment
Deployment

The outer node is where the code runs. Each base image points to what it builds, solid arrows lead to databases and dashed ones to outside services. Rendered with uml-mcp.

PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
node "Docker Compose" as H {
  node "artemis-switch-build" as N0 <<container>>
}
database "Active Record" as DB0
N0 --> DB0
cloud "Console network stack (libnx/…" as E0
N0 ..> E0
cloud "WebSocket" as E1
N0 ..> E1
@enduml

Where it runs

Published to

Compose services

artemis-switch-build

7.13. Monitoring

What the code logs, measures and exposes to tell whether it is working.

7.14. Security

Controls

Supply chain

Environment variables 3

The page lists names without their values and underlines the ones that look like secrets.

DRM_DEV HOME USERPROFILE

7.15. Testing

Test files
78
Source files
567
Unit tests
75
Benchmark tests
2
Integration tests
1

Code coverage

No measured coverage report in the analyzed sources.

Where the tests are

tests 78

Continuous integration

Lint
ruff
UML CI pipeline
CI pipeline

The main GitHub Actions workflow runs these steps. Jobs that run in parallel sit side by side. Rendered with uml-mcp.

PlantUML source
@startuml
skinparam backgroundColor transparent
skinparam shadowing false
skinparam roundCorner 10
skinparam defaultFontName Arial
skinparam defaultFontSize 13
skinparam ArrowColor #4e6070
skinparam ArrowFontColor #3d4f5f
skinparam titleFontSize 14
skinparam actorBorderColor #006a62
skinparam actorBackgroundColor #e9f3f4
skinparam usecaseBorderColor #006a62
skinparam usecaseBackgroundColor #e9f3f4
skinparam componentBorderColor #006a62
skinparam componentBackgroundColor #e9f3f4
skinparam interfaceBorderColor #006a62
skinparam interfaceBackgroundColor #ffffff
skinparam nodeBorderColor #4e6070
skinparam nodeBackgroundColor #f4f8fb
skinparam databaseBorderColor #006a62
skinparam databaseBackgroundColor #ffffff
skinparam cloudBorderColor #4e6070
skinparam cloudBackgroundColor #f0f5fa
skinparam artifactBorderColor #006a62
skinparam artifactBackgroundColor #ffffff
skinparam rectangleBorderColor #b0c2d3
skinparam activityBorderColor #006a62
skinparam activityBackgroundColor #e9f3f4
skinparam partitionBorderColor #b0c2d3
skinparam stereotypeCBackgroundColor #ffffff
title Feature and Integration CI
start
fork
  partition "Consolidated portable integra…" {
    :Checkout;
    :Validate localization JSON syntax;
    :Validate Artemis English/French translation…;
    :Validate release/CD contract;
    :Validate isolated VPN build contract;
    :Configure unit suite;
    :Build unit suite;
    :Run unit suite;
  }
fork again
  partition "Release source package contra…" {
    :Checkout repository and submodules;
    :Build dry-run source packages;
    :Inspect source packages;
  }
fork again
  partition "ASan + UBSan integration" {
    :Checkout;
    :Configure;
    :Build;
    :Test;
  }
fork again
  partition "Artemis CI summary" {
    :Require all integration jobs;
  }
end fork
stop
@enduml

8. Types and file structure

651 declared types, with 12 in the base-relationship overview.

Type diagram
classDiagram
direction TB
class t27b200e5e641["AboutTab"] {
}
class t1d8fa37de9f9["ActiveRemoteRoute"] {
  <<struct>>
}
class t76b3bc05a24d["AddHostTab"] {
  findHost()
  stopSearchHost()
  connectHost()
  fillSearchBox()
}
class t71f8fd94394b["AppCell"] {
  setFavorite()
  updateFavoriteAction()
}
class tcb6c23872ee9["AppListView"] {
  onLayout()
  willAppear()
  draw()
  blockInput()
}
class t5416fcf29d4d["ArtemisSettingsTab"] {
  refreshValues()
  editWidth()
  editHeight()
  refreshRemoteAccessRows()
}
class taa0a005beea4["ButtonSelectingDialog"] {
  open()
  reloadLabel()
  buttonsText()
  draw()
}
class t0cab5ff3d09b["DiscoveryCandidate"] {
  <<struct>>
  isDirect()
}
class t9649c53d1d2d["FavoriteTab"] {
  refreshIfNeeded()
  setRefreshNeeded()
  updateAppList()
}
class tae773a498879["ClickGestureRecognizer"] {
  recognitionLoop()
}
class t03d6e2ef334c["FingersGestureRecognizer"] {
  recognitionLoop()
}
class t96b64c4eb4d8["TwoFingerScrollState"] {
  <<struct>>
}
class b66d2434ea20a["brls::Box"] {
  <<unresolved_base>>
}
b66d2434ea20a <|-- t27b200e5e641
class b12e371ab4244["brls::Box"] {
  <<unresolved_base>>
}
b12e371ab4244 <|-- t76b3bc05a24d
class b9fb6426c89d6["Box"] {
  <<unresolved_base>>
}
b9fb6426c89d6 <|-- t71f8fd94394b
class bd9fae1c39e24["Box"] {
  <<unresolved_base>>
}
bd9fae1c39e24 <|-- tcb6c23872ee9
class bc8de2cffa73c["brls::Box"] {
  <<unresolved_base>>
}
bc8de2cffa73c <|-- t5416fcf29d4d
class b4629afd0f9ec["Dialog"] {
  <<unresolved_base>>
}
b4629afd0f9ec <|-- taa0a005beea4
class b3def5b4c744e["brls::Box"] {
  <<unresolved_base>>
}
b3def5b4c744e <|-- t9649c53d1d2d
class b1c2ec6b5bcbd["brls::GestureRecognizer"] {
  <<unresolved_base>>
}
b1c2ec6b5bcbd <|-- tae773a498879
class b905c043cc5ee["brls::GestureRecognizer"] {
  <<unresolved_base>>
}
b905c043cc5ee <|-- t03d6e2ef334c

Unresolved bases remain labelled; full signatures are not reconstructed.

9. Reference

616 / 616 eligible files read (962 in the tree); 0 oversized, 0 failed reads and 88 syntax diagnostics.

What the scan covers

Known dependency/build folders, credential files and binaries are excluded. The scan covers READMEs, recognized manifests and parser-supported source files.

Linguist statistics cover the whole repository and may include other languages.

11. Files to read 3

12. Glossary

Repository

The versioned source files, configuration, and history of a project.

README

The repository’s entry document, usually covering its purpose and initial setup.

Dependency

A library, tool, or service that another component needs to build or run.

Session

State kept across related requests, often to track a signed-in user. Authentication and permission checks remain separate concerns.

Published port

A host port mapped to a service’s container port. Publishing a port can expose that service beyond its container network.

CLI

A text interface in which commands and arguments select operations. Exit codes let scripts detect success or failure.

Build

The steps that turn source files and dependencies into a runnable program or distributable artifact.

GPU

A processor designed for parallel workloads, including graphics rendering and many numerical operations.

Shader

A program executed at a stage of a graphics or compute pipeline, for example to transform vertices or calculate pixel colors.

Video decoder

A component that reconstructs video frames from a compressed stream. Decoding and displaying a frame are separate stages.

Backend

An implementation behind a shared interface. Depending on context, it can mean a server-side service or a graphics, input, or storage implementation.

Firmware

Software running on a device or microcontroller to control its hardware and communication.

Fork

A separate repository derived from another project. A change in a fork is not automatically a change in the upstream project.

13. References

  1. antoinebou12. (2026, November 5). Artemis-Switch. [Software repository] github.com.
  2. antoinebou12. (2026, November 5). Artemis-Switch: README. [Repository documentation] github.com.
  3. antoinebou12. (2026, November 5). app/src/main.cpp. [Source file] github.com.
  4. antoinebou12. (2026, November 5). app/src/streaming/MoonlightSession.cpp. [Source file] github.com.
  5. antoinebou12. (2026, November 5). app/src/streaming/switch/SwitchMoonlightSessionDecoderAndRenderProvider.cpp. [Source file] github.com.
  6. Boucher, A. (2026, October 20). Artemis Switch: following a frame from the network to the screen. [Project article] antoineboucher.info.
  7. Boucher, A. (2026, September 2). Handheld streaming and homebrew — Artemis-Switch, moonlight-N3DS, sm64coopdx-switch. [Project article] antoineboucher.info.
  8. Boucher, A. (2026, September 2). Artemis-Switch. [Project article] antoineboucher.info.
  9. Boucher, A. (2026, October 23). Artemis-Switch: A streamed frame from host to screen. [Project article] antoineboucher.info.

Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.