A modular, real‑time quiz and show platform built on .NET 10, Blazor, and Svelte.
Explore the docs »
Get Started
·
Report Bug
·
Request Feature
Table of Contents
Nuotti is a multi‑project .NET solution for running interactive quizzes and live shows. It features a backend with real‑time messaging, an audience web app, a projector display app, an audio engine, and a set of shared contracts. The system is designed around events and reducers, with testing coverage for contracts and backend behavior.
Key capabilities suggested by the codebase:
- Real‑time communication via SignalR hubs (e.g., QuizHub).
- Session management and an event bus, with an in‑memory implementation for local development.
- A Blazor WebAssembly audience app (built with MudBlazor).
- A projector/host display app (Avalonia desktop).
- A CLI simulation kit for driving scripted interactions against the backend.
This repository targets .NET 10 (the SDK version is pinned in global.json) and uses modern
ASP.NET Core features.
- .NET 10 SDK (release candidate — pinned in
global.json) - ASP.NET Core / SignalR
- Blazor WebAssembly + MudBlazor (Audience)
- SvelteKit (the static
webapp) - Avalonia (Projector desktop app)
- .NET Aspire (one‑command local orchestration)
- C# 14
Top‑level notable projects and folders:
- Nuotti — .NET Aspire AppHost that orchestrates the full local stack (backend, clients, and backing services).
- Nuotti.Backend — ASP.NET Core backend with endpoints, sessions, eventing, rate limiting, and hub broadcasting.
- Nuotti.Audience — Blazor WebAssembly UI for participants.
- Nuotti.Projector — Avalonia display surface for the show/host/projector.
- Nuotti.AudioEngine — Audio playback/engine components.
- Nuotti.Contracts — Shared messages, models, events, reducers, and web shared types.
- Nuotti.SimKit — CLI simulator (
nuotti-sim) for scripted interactions against the backend. - ServiceDefaults — Shared Aspire service configuration (telemetry, health checks, resilience).
- web — SvelteKit static web app.
- tests — Cross‑cutting unit, integration, and end‑to‑end test projects.
- Nuotti.*.Tests — Per‑project test suites (contracts, backend, projector, and more).
- docs — Documentation and design notes.
Follow these instructions to set up a local development environment.
- .NET SDK 10 (the exact release‑candidate version is pinned in
global.json)- Verify:
dotnet --versionshould print the pinned 10.x version
- Verify:
- Node.js 20+ (only if you plan to work on the
webSvelteKit app) - Docker (only if you want the one‑command Aspire stack, which starts Postgres, Redis, and an Azure Storage emulator)
- Clone the repo
git clone https://github.com/sifterstudios/nuotti.gitcd nuotti
- Ensure the correct .NET SDK is used
- This repo provides a
global.json;dotnet --infoshould list the pinned .NET 10 SDK.
- This repo provides a
- Restore and build
dotnet restoredotnet build Nuotti.sln -c Debug
The quickest way to start everything at once is the .NET Aspire AppHost (requires Docker, since it also starts Postgres, Redis, and an Azure Storage emulator):
- One‑command local stack:
dotnet run --project Nuotti- Open the Aspire dashboard URL printed in the console to reach each service.
Alternatively, run projects individually in separate terminals:
-
Backend (API + SignalR hub):
dotnet run --project Nuotti.Backend- By default this listens on http://localhost:5240. Run standalone, it uses in‑memory stores, so no database or Docker is required.
-
Web (SvelteKit static app):
cd web && npm install && npm run dev- Navigate to the URL shown in the console (typically http://localhost:5173).
-
Audience (Blazor WebAssembly app):
dotnet run --project Nuotti.Audience- Navigate to the URL shown in the console.
-
Performer (Blazor Server app):
dotnet run --project Nuotti.Performer- Then open the shown URL and enter your Backend URL (e.g., http://localhost:5240) and Session code (or create a new session).
- The Performer UI was scaffolded following patterns from HAVIT's NewProjectTemplate-Blazor:
-
Projector:
dotnet run --project Nuotti.Projector
-
Audio Engine:
dotnet run --project Nuotti.AudioEngine
-
Simulator (CLI):
dotnet run --project Nuotti.SimKit -- run --backend http://localhost:5240 --session dev
Tip: Use your IDE run configurations (e.g., JetBrains Rider) to start multiple projects.
- Start Backend and Audience.
- Create or join a session from the Audience app (session code such as "dev").
- Use the Projector to display the show view.
- The Simulator (
nuotti-sim) can drive scripted interactions against a running backend, withbaseline,load, andchaospresets. Run it with--helpto see all options.
Developers can explore the eventing model in Nuotti.Backend/Eventing and shared contracts in Nuotti.Contracts/V1 to understand how state changes flow through the system.
Nuotti can be deployed to production using Docker Compose with pre-built images from GitHub Container Registry.
See deploy/README.md for deployment documentation, including:
- Unraid setup with Docker Compose
- Configuration options
- Reverse proxy setup
- Monitoring and troubleshooting
The Unraid UI walkthrough lives in deploy/UNRAID-UI-GUIDE.txt.
GitHub Actions automatically builds and publishes Docker images to GHCR on every push to main:
ghcr.io/sifterstudios/nuotti-backend:latestghcr.io/sifterstudios/nuotti-audience:latestghcr.io/sifterstudios/nuotti-web:latest
Images are built for linux/amd64 to match the self‑hosted Unraid runner.
- Add Docker compose for one‑command startup.
- CI/CD pipeline (build, test, publish artifacts).
- Implement Simulator logic in Nuotti.SimKit (connect to hub, drive flows).
- Expand documentation in /docs with diagrams and message flows.
- More game modes and audience interactions.
See the open issues for a full list of proposed features and known issues.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/awesome-feature) - Commit your Changes (
git commit -m 'feat: add some awesome feature') - Push to the Branch (
git push origin feature/awesome-feature) - Open a Pull Request
Please consider conventional commits for clear history. For larger changes, open an issue first to discuss what you would like to change.
This repository does not yet include a LICENSE file, so no license has been formally granted.
If you would like to use this project, please open an issue to discuss licensing.
Project Link: https://github.com/sifterstudios/nuotti
Feel free to open an issue or start a discussion.
- Best README Template by Othneil Drew: https://github.com/othneildrew/Best-README-Template
- .NET & ASP.NET Core teams
- JetBrains Rider for a stellar .NET IDE
You can test the exact same build that runs in CI on your machine.
Prereqs:
- Docker Desktop 4.x (Compose V2) or Docker Engine + docker compose plugin
- PowerShell (recommended on Windows)
Quick start (Windows):
- Build only:
- powershell -ExecutionPolicy Bypass -File .\tools\build-local.ps1
- Build and run:
- powershell -ExecutionPolicy Bypass -File .\tools\up-local.ps1
- Stop and clean up:
- powershell -ExecutionPolicy Bypass -File .\tools\down-local.ps1
- Add -Prune to also prune dangling images.
What this does:
- Uses
deploy/docker-compose.local.ymlto build and run the backend, audience, and web images locally - Builds the images with
docker compose build --pull, then (with-Up) starts them detached
Services and URLs after up:
- API: http://localhost:5210
- Audience: http://localhost:5280
- Web: http://localhost:5380
Troubleshooting:
- If a port is in use, change the host port mapping in
deploy/docker-compose.local.yml - See container logs:
docker compose -f deploy/docker-compose.local.yml logs -f