Sucrose.Backgroundog.exe is the headless, long-running background service behind Sucrose's audio-reactive and system-status APIs. It continuously collects machine telemetry (CPU, GPU, RAM, network, storage, battery, BIOS, motherboard, date) plus the now-playing audio metadata and a live FFT audio spectrum, then pushes that data to the running wallpaper over one of three interchangeable transports (Pipe, Signal, or Transmission). It also acts as the engine that enforces Sucrose's performance/pause rules — deciding when to pause, suspend, or close the wallpaper based on conditions like screen lock, full-screen apps, battery level, and CPU/GPU/RAM thresholds. This page is for developers and power users who want to understand exactly what Backgroundog does, what data it emits, and how its timing and settings work.
Backgroundog is a console (hidden) process with a long-running loop. It is not started directly; it is launched on demand through the command bus when a wallpaper needs it:
✔Backgroundog✖<path-to-Sucrose.Backgroundog.exe>
The Launcher/engine spawns it (via Commandog) when PerformanceCounter is enabled, before the engine itself starts.
Startup sequence (App.Main):
SSDHG.Configure() (high-performance GPU) + SSDHR.Configure() (private runtime), UTF-8 console encoding, set thread culture from the Culture setting.Instance.Basic(Mutex.Backgroundog, App.Backgroundog) — the global mutex {Sucrose-Wallpaper-Engine-Backgroundog} plus a WorkCount <= 1 check (see Single-Instance & Mutexes).Security.Apply().Initialize.Start(), then the loop: do { Initialize.Dispose(); await Task.Delay(AppTime = 1000 ms); } while (Exit).Initialize.Stop().Internal.Exit defaults to true. When there is no live wallpaper after a retry sequence, Attempt.Start() sets Exit = false and the service terminates itself — Backgroundog never lingers without a wallpaper to feed.
flowchart TD
Cmd["Commandog: ✔Backgroundog✖<path>"] --> Gate{"Instance.Basic<br/>single-instance gate"}
Gate -->|already running| Stop1["exit"]
Gate -->|first instance| Init["Initialize.Start()"]
Init --> Loop["Loop: AppTime = 1000 ms<br/>InitializeTimer tick = 100 ms"]
Loop --> Spec["Specification.Start()<br/>collect all sensors"]
Spec --> Pick{"live wallpaper?"}
Pick -->|"running, not paused"| Perf["Performance.Start()<br/>should a rule trip?"]
Pick -->|"paused / closed"| Cond["Condition.Start()<br/>should it resume?"]
Pick -->|none| Attempt["Attempt.Start()<br/>retry Run() up to 5x"]
Attempt -->|nothing starts| Exit["Exit = false<br/>service self-terminates"]
Perf --> Loop
Cond --> Loop
All timing constants live in Manage/Internal.cs (milliseconds):
| Constant | Value | Meaning |
|---|---|---|
AppTime |
1000 |
Outer loop delay. |
InitializeTime |
100 |
Base tick unit; the InitializeTimer fires every 100 ms. |
InitializeDueTime |
3000 (100 × 30) |
Delay before the first tick. |
SpecificationLessTime |
1000 |
Cadence for light sensors. |
SpecificationTime |
2000 |
Cadence for standard sensors. |
SpecificationMaxTime |
3000 |
Re-entrancy cool-down for the heavy pass. |
SpecificationPeakTime |
10000 |
Cadence for expensive sensors (e.g. storage WMI). |
Each tick (Initialize.InitializeTimer_Callback):
Specification.Start() — collect all sensors as fire-and-forget tasks.Processing re-entrancy flag allows it, run exactly one of:Performance.Start() — when a live wallpaper is running and not already paused (checks whether a rule should trip).Condition.Start() — when already paused/closed (checks whether to resume).Attempt.Start() — when there is no live wallpaper.A Processing flag ensures only one heavy pass runs at a time, then waits SpecificationMaxTime.
Specification.Start() launches each sensor in its own Task.Run, guarded by a per-sensor boolean flag (e.g. BiosManagement, PingManagement, AudioManagement, ProcessorManagement2, GraphicManagement2, NetworkManagement2, StorageManagement2, etc.) so a slow sensor never blocks the rest.
| Sensor | Source | Notes |
|---|---|---|
| BIOS | WMI Win32_BIOS |
Name, Caption, Version, Description, ReleaseDate, Manufacturer, SerialNumber, CurrentLanguage. One-shot. |
| Date | DateTime.Now |
Refreshed every tick. |
| Ping | Skylark.Standard.Extension.Ping |
Host chosen by the PingType setting (default "Bing"); 1000 ms timeout. Only when online (Network.GetHostEntryAsync). |
| Audio | Playhead + WASAPI | Only when AudioRequired is on. See the two sections below. |
| Memory | GlobalMemoryStatusEx (Skylark.Wing) |
Physical + virtual (page file) in GB; MemoryLoad %. |
| Battery | WMI Win32_Battery + Skylark.Wing.Utility.Power |
AC status, energy saver, life %, power plan. |
| Graphic | VorticeAdapterEnumerator (DXGI) + "GPU Engine" perf counters |
Adapter list persisted to the GraphicInterfaces system setting; first adapter auto-selected; usage read by LUID and clamped to 0–100. |
| Network | Network.InstanceNetworkInterfaces() + "Network Interface" perf counters |
Interface list persisted to NetworkInterfaces; first auto-selected; "Bytes Sent/sec" & "Bytes Received/sec" → Upload/Download/Total. |
| Storage | WMI Win32_LogicalDisk + "LogicalDisk"/"PhysicalDisk" perf counters |
Collected on the slow (SpecificationPeakTime, 10 s) cadence. |
| CPU | WMI Win32_Processor + "Processor / % Processor Time" perf counters |
_Total and per-core (0..ProcessorCount-1); builds the Cores array with min/max flags. |
| Motherboard | WMI Win32_BaseBoard |
Product, Version, Manufacturer; Name = "<Manufacturer> <Product>". |
| Remote / Virtual / FullScreen / Focus | Skylark.Wing helpers | Collected only when the matching performance rule is non-Resume (or already active). |
Perfomance-counter collection is gated by the PerformanceCounter setting (default true).
The audio spectrum is produced by Extension/AudioVisualizer.cs and only runs when AudioRequired is on.
NAudio.Wave.WasapiLoopbackCapture records the system output ("what you hear"). The class implements IMMNotificationClient and recreates the capture when the default render device changes (OnDefaultDeviceChanged for DataFlow.Render).DataAvailable callback; the FFT + emit are throttled to ~60 Hz, i.e. once every EmitIntervalMs = 16 ms):FftSize = 4096, decoupled from the WASAPI callback buffer size so the bin→frequency mapping stays deterministic. Sample reads are format-robust (float32 / PCM16 / PCM32).MathNet.Numerics.IntegralTransforms.Fourier.Forward with FourierOptions.NoScaling (keeps the magnitude scale deterministic for the full-scale reference).MaxSample = 128 logarithmic frequency bands spanning 20 Hz – 16 kHz (peak magnitude per band), computed for mono, left, and right. Mono = |(L + R) / 2|.Finalize: normalise each band to [0, 1] (linear magnitude × high-frequency tilt ÷ full-scale reference × LinearGain = 2.5, minus NoiseFloor = 0.01, then clamped), apply asymmetric attack/decay smoothing (AttackSpeed = 0.35 fast rise, ReleaseSpeed = 0.06 slow fall) per bin, then neighbour-bin smoothing (HorizontalSmoothness = 1) → a double[128] array per channel.AudioDataAvailable, storing the three arrays into AudioData.Data (mono), AudioData.DataLeft, and AudioData.DataRight.The result is the 128-sample mono spectrum exposed to web wallpapers as Audio.Data, with the per-channel Audio.DataLeft / Audio.DataRight alongside it. All three are 128 bins logarithmically mapped across 20 Hz – 16 kHz, each normalised to [0, 1]; Data[0] is the lowest (bass) band and an all-zero array means silence. A high-frequency tilt (+2 dB/octave) keeps treble content visible against music's natural roll-off. Libraries: NAudio, MathNet.Numerics. See Audio API for the JavaScript side.
Extension/AudioSession.cs uses Playhead (NowPlayingSessionManager / MediaPlaybackDataSource). On SessionListChanged it reads:
MediaObjectInfo — Title, Artist, Subtitle, AlbumTitle, AlbumArtist, TrackNumber, AlbumTrackCount, MediaType.MediaPlaybackInfo — RepeatMode, PlaybackRate/Mode/State, ShuffleEnabled, PropsValid, LastPlayingFileTime. (SourceAppId is read from the NowPlayingSession, not from MediaPlaybackInfo.)MediaTimelineProperties — EndTime, Position, StartTime, Min/MaxSeekTime (reported in TotalMilliseconds), plus PositionSetFileTime (a DateTime, serialized as-is — not a millisecond value).AudioData.ThumbnailString.AudioData.State indicates whether a media session currently exists.
After collection, Specification.Start() builds a single payload of 10 JObject fields and pushes it over whichever channel(s) are enabled. All channels are gated on !Condition (data is not sent while the wallpaper is paused/closed).
| Channel | Enabled when | Target |
|---|---|---|
| Pipe | PipeRequired |
BackgroundogManager.StartClient(json) → pipe "Sucrose.Wallpaper.Engine.Backgroundog.Pipe". |
| Signal | SignalRequired |
BackgroundogManager.FileSave<…>(…) → writes Backgroundog-<guid>.sgnl under %AppData%\Sucrose\Cache\SignalT. |
| Transmission | TransmissionRequired |
TCP to Loopback:TransmissionPort. Default transport. |
The active transport is selected by the CommunicationType setting (default Transmission) on the engine side; the producer reads the *Required/TransmissionPort flags the engine sets. The payload is JSON-serialized with Newtonsoft (Formatting.None, TypeNameHandling.None). Only the WebView and CefSharp engines consume this channel (#if LIVE_WEBVIEW || LIVE_CEFSHARP). See IPC for the full transport mechanics.
flowchart LR
Spec["Specification.Start()<br/>builds 10-JObject payload<br/>Bios/Date/Audio/Memory/Battery/<br/>Graphic/Network/Storage/Processor/Motherboard"]
Spec -->|PipeRequired| Pipe["StartClient(json)<br/>Backgroundog.Pipe"]
Spec -->|SignalRequired| Signal["FileSave<br/>Backgroundog-<guid>.sgnl"]
Spec -->|TransmissionRequired| Trans["TCP loopback<br/>: TransmissionPort"]
Pipe --> Web["WebView / CefSharp<br/>Web.cs server"]
Signal --> Web
Trans --> Web
Web --> Exec["Compatible hooks<br/>ExecuteScriptAsync"]
Exec --> JS["Wallpaper JS<br/>SucroseAudioData / Sucrose<X>Data"]
The 10 JObjects (Pipe.Interface.Backgroundog) are: Bios, Date, Audio, Memory, Battery, Graphic, Network, Storage, Processor, Motherboard. Their inner field names (from Extension/Data.cs) are the public data contract for wallpaper authors:
| Object | Fields |
|---|---|
| Bios | Name, State, Caption, Version, Description, ReleaseDate, Manufacturer, SerialNumber, CurrentLanguage |
| Date | Day, Hour, Year, Month, State, Minute, Second, Kind, DayOfYear, Millisecond, DayOfWeek |
| Audio | State, Title, Artist, Subtitle, AlbumTitle, Data, DataLeft, DataRight, AlbumArtist, SourceAppId, TrackNumber, MediaType, PlaybackRate, PropsValid, RepeatMode, ShuffleEnabled, PlaybackMode, AlbumTrackCount, ThumbnailString, PlaybackState, EndTime, Position, StartTime, LastPlayingFileTime, PositionSetFileTime, MaxSeekTime, MinSeekTime |
| Memory | Name, State, MemoryLoad, MemoryUsed, VirtualName, MemoryAvailable, VirtualMemoryLoad, VirtualMemoryUsed, VirtualMemoryAvailable |
| Battery | Name, State, Status, Chemistry, SavingMode, BatteryFlag, Description, LifePercent, SaverStatus, FullLifetime, ACPowerStatus, DesignVoltage, LifeRemaining, ChargeStatus, BatteryLifeTime, PowerPlanType, EstimatedRunTime, EnergySaverType, PowerLineStatus, BatteryLifePercent, BatteryFullLifeTime, EstimatedChargeRemaining |
| Graphic | Max, Min, Now, Name, State, Manufacturer |
| Network | Host, Name, Ping, State, Total, Online, Upload, Download, PingData, TotalData, UploadData, PingAddress, DownloadData, FormatTotalData, FormatUploadData, FormatDownloadData |
| Storage | State, Drivers, LogicalDrivers, PhysicalDrivers (each a JArray of disks) |
| Processor | Max, Min, Now, Core, Name, Type, Cores, State, Thread, CoresMax, CoresMin, CoresNow, ProcessorCount |
| Motherboard | Name, State, Product, Version, Manufacturer |
Every object carries a State boolean indicating whether its data is valid for the current tick. The Audio.Data field is the 128-sample mono FFT spectrum described above; Audio.DataLeft / Audio.DataRight are the per-channel spectra (same 128-value [0, 1] shape). For the JavaScript-side shape of these objects, see System API and Audio API.
Backgroundog reads its configuration from the Backgroundog settings store. Keys, defaults, and clamp ranges:
| Setting | Default | Range / Type | Effect |
|---|---|---|---|
AudioRequired |
false |
bool | Enable audio capture + FFT visualizer. |
PipeRequired |
false |
bool | Enable Pipe output. |
SignalRequired |
false |
bool | Enable Signal output. |
TransmissionRequired |
false |
bool | Enable TCP (Transmission) output. |
TransmissionPort |
0 |
0–65535 | Loopback port (set dynamically at engine start). |
CommunicationType |
Transmission |
Pipe / Signal / Transmission |
Active live-data transport. |
PerformanceCounter |
true |
bool | Enable perf-counter sensors + start Backgroundog with the wallpaper. |
PingType |
"Bing" |
string (host key) | Host used for the network ping. |
PingValue |
100 |
0–1000 (ms) | Ping threshold for the Network performance rule. |
UploadValue |
800 |
0–99999999 | Upload threshold. |
UploadType |
Kilobyte |
Skylark.Enum.StorageType |
Upload threshold unit. |
DownloadValue |
10 |
0–99999999 | Download threshold. |
DownloadType |
Megabyte |
StorageType | Download threshold unit. |
ProcessorUsage |
70 |
0–100 (%) | CPU pause threshold. |
GraphicUsage |
70 |
0–100 (%) | GPU pause threshold. |
MemoryUsage |
80 |
0–100 (%) | RAM pause threshold. |
BatteryUsage |
50 |
0–100 (%) | Battery pause threshold. |
NetworkAdapter |
"" |
string | Selected network adapter (auto-selected). |
GraphicAdapter |
"" |
string | Selected GPU adapter (auto-selected). |
PausePerformance / ClosePerformance |
false |
bool | Transient state flags written by the service. |
The per-trigger performance modes (LockPerformance, SleepPerformance, BatteryPerformance, etc.) and PausePerformanceType are surfaced on the Settings → Performance page. See Performance Rules for what each rule does.
Backgroundog owns the auto-pause logic. Performance.Start() evaluates rules in this fixed order:
Lock → Focus → Sleep → Memory → Remote → Battery → Console →
Graphic → Network → Session → Virtual → Processor → FullScreen →
ScreenSaver → BatterySaver
Each rule is skipped if its mode is Resume. Otherwise the service polls the condition for a rule-specific MaxCount (instant 0 for Lock/Sleep/Console/Session/ScreenSaver; 5 for most others; threshold-based for Memory/Graphic/CPU/Battery/Network). On a trip it sets the CategoryPerformance, sets Condition = true, and calls Lifecycle():
Close → set ClosePerformance, call Live.Helper.Kill.Stop() (close the wallpaper).Pause → set PausePerformance. If PausePerformanceType.Heavy, the live process is suspended (via Space.Extension.Lifecycle.Suspend), including WebView2/CefSharp child processes whose command line contains "Sucrose" and the Aurora application process. Light pauses without suspending threads.In both cases the Launcher tray icon is notified over Signal (Icon = true).
Condition.Start() is the mirror: while in a category it polls until the condition clears, then resumes — Close restarts the wallpaper (Run.Start()), Pause/Heavy resumes the process (Lifecycle.Resume(...)), and Condition is cleared. Tray icon Signal Icon = false.
Attempt.Start() runs when there is no live wallpaper: it retries Space.Helper.Live.Run() up to 5 times (1 s apart, 3 s settle on success). If nothing starts, it sets Exit = false, clears the two transient pause flags, and the service stops.
The enum values behind these rules (src/Shared/Sucrose.Shared.Dependency/Enum/PerformanceType.cs):
PerformanceType: Close, Pause, Resume.PausePerformanceType: Heavy, Light.NetworkPerformanceType: Not, Ping, Upload, Download.CategoryPerformanceType: Not, Lock, Focus, Sleep, Memory, Remote, Battery, Console, Graphic, Network, Session, Virtual, Processor, FullScreen, ScreenSaver, BatterySaver.HighPerformanceType: Default, PowerSaving, HighPerformance.Extension/WindowsListener.cs implements Skylark.Wing.Interface.IEventListener and feeds session events into the performance engine:
OnLogon / OnLogoff → WindowsSession.OnDisplayLock / OnDisplayUnlock → WindowsLock.OnStartScreenSaver / OnStopScreenSaver → WindowsScreenSaver.It is started/stopped by Extension/Windows.cs (Start() / Stop()).