Skip to content

LightSpace

Photo, Video & Motionv0.8.0-alpha.1 MIT 3

A local-first photography workspace for desktop and browser

LightSpace is a shared Uno desktop/WebAssembly photography application: browse a local catalog, develop a photograph, refine its composition and masks, and export a rendered copy while the original bytes stay untouched. A dark workspace combines a library sidebar, photo canvas, filmstrip, histogram and development inspector, powered by GPU-capable SkSL effects and eight reusable C# libraries.

8reusable C# libraries
17development sliders
32curve points per RGB channel
8local masks per photo
LightSpacewieslawsoltes.github.io/LightSpace/
LightSpace running in a browser
Highlights

What makes LightSpace special

Familiar if you know Lightroom-style photo workflows — but original, local‑first and written end‑to‑end in C#.

Non-destructive development

Light, white balance, vibrance, curves, eight color bands and effects are stored as settings; original encoded bytes are never modified.

Local photo catalog

Import, search, rate, flag and group photos into albums in a paged grid and filmstrip that runs entirely on your device.

Four-way color grading

Shadow, midtone, highlight and global wheels with luminance, blending and balance controls.

Arbitrary RGB curves

Master, red, green and blue curves with up to 32 points each and linear or shape-preserving smooth interpolation.

Masks and brushes

Radial and linear gradients, luminance and sampled Oklab color ranges, and pressure-aware brush masks with local adjustments.

GPU-capable SkSL effects

Compiled SkSL runtime effects render inside Uno's Skia composition path, with software rendering for tests and export.

XMP sidecars

Import and export standard metadata and a reported Camera Raw subset, with a native extension for full LightSpace round trips.

Content-addressed recovery

Originals stored under SHA-256 keys, atomically published edit manifests and integrity checks on restore.

0reusable libraries
0lines of C# & XAML
0features catalogued
0.NET projects
0CI workflows
0docs
Feature tour

28 capabilities, 6 areas

Organize

  • Import JPEG, PNG, WebP, BMP or the first GIF frame, retaining original bytes
  • Paged grid, detail view and filmstrip with 60-photo pages
  • Search filenames, captions and keywords
  • Ratings, pick/reject flags and albums
  • Copy/paste and sync development settings across selected photos

Develop

  • Light, relative white balance, vibrance/saturation and monochrome
  • Eight-band HSV color mixer with hue, saturation and luminance
  • Four-way color grading with blending and balance
  • Creative presets, texture/clarity/dehaze approximations and sharpening
  • Spatial smoothing, vignette and grain; double-click resets a slider

Curves

  • Master, red, green and blue curves with up to 32 points per channel
  • Linear or shape-preserving smooth interpolation
  • Add, drag and delete points or type Input/Output values
  • 2048-entry floating-point lookup cache reused across unrelated edits

Crop, clone and compare

  • Free crop rectangles with handles, centered ratios, quarter turns and flips
  • Up to 32 feathered clone stamps from an explicit Alt-click source
  • Original view and a draggable before/after divider
  • Fit, source-pixel 100% zoom and wheel zoom around the pointer

Masks

  • Up to eight masks with enable, amount, inversion, rename and duplicate
  • Rotatable radial and linear gradients with on-canvas handles
  • Luminance ranges and up to five sampled Oklab color pins
  • Pressure-aware brush masks with size, feather, flow and density
  • Local exposure, contrast, temperature, tint and saturation

Preserve and export

  • One transaction per gesture with 100-step undo/redo and named versions
  • Export 8-bit sRGB JPEG, PNG or WebP copies or a ZIP of a selection
  • Portable .lightspace catalogs with originals, edits, albums and versions
  • XMP sidecar import with a compatibility report before applying
  • IndexedDB or native recovery with source integrity checks and retry
Integrated playground

Run LightSpace right here

Open the live WebAssembly build and develop the demonstration catalog — every edit is non-destructive and stays on your device. Switch viewports, go fullscreen and tick off the guided tour as you explore.

https://wieslawsoltes.github.io/LightSpace/
LightSpace screenshot

Try LightSpace live

Open the live WebAssembly build and develop the demonstration catalog — every edit is non-destructive and stays on your device.

Real C# · Uno WebAssembly · runs locally in your browser
Booting .NET WebAssembly runtime…
Reusable packages

8 libraries you can embed

LightSpace is assembled from independently reusable libraries, each published on NuGet.org. Hover the graph to trace dependencies; click a node to jump to its package, and open a card for install and usage.

Core 1,075 lines Catalog 383 lines Editing 592 lines Imaging 116 lines Rendering.Skia 707 lines Storage 112 lines Controls 2,188 lines Workbench 1,621 lines App 255 lines
Arrows point from a project to what it references. Bars show relative size. ModelI/OEngineRenderingServiceControlsWorkbench
Model 0.4.1-alpha.1

LightSpace.Core

Photo state, grading, curves/interpolation, masks, brush samples, geometry and presets

No UI depsnet10.0
1,075 lines17 files0 refs87 downloadsUsage
I/O 0.4.1-alpha.1

LightSpace.Catalog

Schema migration, serialization, queries and XMP metadata/settings interchange

No UI depsnet10.0
refs → Core
383 lines7 files1 refs91 downloadsUsage
Engine 0.4.1-alpha.1

LightSpace.Editing

Gesture transactions, bounded undo/redo, versions, synchronization and revision-aware recovery

No UI depsnet10.0
refs → Catalog, Storage
592 lines9 files2 refs74 downloadsUsage
I/O 0.4.1-alpha.1

LightSpace.Imaging

Bounded decoding, EXIF orientation, sRGB conversion and fallback artwork

SkiaSharpnet10.0
refs → Core
nuget → SkiaSharp
116 lines2 files1 refs80 downloadsUsage
Rendering 0.4.1-alpha.1

LightSpace.Rendering.Skia

SkSL runtime effects, image cache, curve tables, incremental brush coverage, histogram and export

SkiaSharpnet10.0
refs → Imaging
707 lines12 files1 refs71 downloadsUsage
Service 0.4.1-alpha.1

LightSpace.Storage

Import/export contracts, atomic native recovery store and optional sidecar-picker capability

No UI depsnet10.0
112 lines4 files0 refs73 downloadsUsage
Controls 0.4.1-alpha.1

LightSpace.Controls

Original Uno chrome, icons, sliders, grading/mixer/curve/mask/brush controls and photo canvas

Uno + Skianet10.0-browserwasmnet10.0-desktop
refs → Editing, Rendering.Skia
2,188 lines30 files2 refs72 downloadsUsage
Workbench 0.4.1-alpha.1

LightSpace.Workbench

Composable workspace, inspectors, commands, dialogs and save UX

Uno + Skianet10.0-browserwasmnet10.0-desktop
refs → Controls, Storage
1,621 lines24 files2 refs85 downloadsUsage
Install from NuGet

8 packages on NuGet.org

All eight libraries are MIT-licensed. Badges show public-feed versions/downloads independently of the source version. Six engine packages target net10.0 without Uno; Imaging and Rendering.Skia use SkiaSharp. Controls and Workbench target desktop/browser with Uno's Skia renderer. Packages are versioned together with .snupkg symbols and SourceLink. Build artifacts do not imply NuGet.org publication. New APIs require packages built from the corresponding source version.

8packages
0.4.1-alpha.1pre-release · Sep 29, 2026
6plain .NET — no UI framework
633downloads
bashStart with the foundation package
dotnet add package LightSpace.Core --prerelease
xmlEvery package — keep only what you need
<PackageReference Include="LightSpace.Core" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Storage" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Catalog" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Imaging" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Rendering.Skia" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Editing" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Controls" Version="0.4.1-alpha.1" />
<PackageReference Include="LightSpace.Workbench" Version="0.4.1-alpha.1" />
xmlCentral package management
<!-- Directory.Packages.props -->
<ItemGroup>
  <PackageVersion Include="LightSpace.Core" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Storage" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Catalog" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Imaging" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Rendering.Skia" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Editing" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Controls" Version="0.4.1-alpha.1" />
  <PackageVersion Include="LightSpace.Workbench" Version="0.4.1-alpha.1" />
</ItemGroup>

Published with NuGet Trusted Publishing from GitHub Actions, with symbols (.snupkg) and SourceLink. Release v0.4.1-alpha.1 ↗

Dependencies: Core ← Catalog, Imaging; Imaging ← Rendering.Skia; Catalog + Storage ← Editing; Editing + Rendering.Skia ← Controls; Controls + Storage ← Workbench. Storage has no project dependencies. The host supplies startup and platform storage. Standard Uno input, scrolling and accessibility primitives remain where appropriate.

Dispose workspaces and native caches. Treat snapshots as copy-on-write and originals as read-only. Objects have one logical owner, normally the UI synchronization context. Component API · Reference API · Recovery invariants

LightSpace.Core

ModelNo UI depsnet10.0v0.4.1-alpha.187 downloads

UI-independent state includes development, crop, optics, geometry, masks, clones and metadata. Curves support linear or shape-preserving interpolation. Grading, ranges, brushes, transfers, Survey layout and copy identity are independent of UI/rendering. Settings normalize supported values.

bashInstall
dotnet add package LightSpace.Core --prerelease
Depends onno other LightSpace packageUsed byCatalogImaging

Basic usage

Key types: PhotoState, DevelopSettings, CropSettings, CloneSpot, GeometrySettings, LensCorrectionSettings, GeometryProjection, ProjectiveTransform, LensMapping, WhiteBalanceEstimator, PointCurve, ChannelCurves, CompiledPointCurve, ColorGradingSettings, GradingTone, ColorBand, LocalMask, ColorRangeSettings, BrushStroke, BrushStrokeBuilder, CatalogDocument, PhotoDocument, VirtualCopyNames, Album, NamedVersion, BuiltInPresets, EditSettingsGroup, EditSettingsTransfer, PhotoNavigationState, SurveyLayout.

csharpUsage
using LightSpace.Core;

var state = new PhotoState
{
    Develop = BuiltInPresets.All.First(p => p.Name == "Golden hour").Settings with { Exposure = .3f },
    Crop = new CropSettings(Left: .05f, Top: .05f, Right: .95f, Bottom: .9f),
    Geometry = new GeometrySettings { Rotate = 1.5f, ConstrainCrop = true },
    Optics = new LensCorrectionSettings(Distortion: 12),
    Masks = [new LocalMask { Name = "Sky", Kind = MaskKind.Linear, Y = .2f, Exposure = -.4f }],
    Rating = 4, Flag = PhotoFlag.Pick, Keywords = ["alps", "sunset"]
}.Normalize();
var develop = state.Develop.Set(nameof(DevelopSettings.Contrast), 20);
var curve = new PointCurve { Points = [new(0, 0), new(.25f, .18f), new(.75f, .82f), new(1, 1)] };
float midtone = curve.Compile().Evaluate(.5f);
(int width, int height) = state.Crop.OutputSize(6000, 4000);
var transfer = new EditSettingsTransfer(state, EditSettingsGroup.Light | EditSettingsGroup.WhiteBalance);
var target = transfer.Apply(new PhotoState { Caption = "Retained target metadata" });

LightSpace.Storage

ServiceNo UI depsnet10.0v0.4.1-alpha.173 downloads

Picker, download and recovery contracts. FileRecoveryStore stages SHA-256 originals and replaces a manifest after required sources exist. No UI or project dependencies.

bashInstall
dotnet add package LightSpace.Storage --prerelease
Depends onno other LightSpace packageUsed byEditingWorkbench

Basic usage

Key types: IWorkspaceStorage (OpenImagesAsync/OpenCatalogAsync/SaveAsync/ReadRecoveryAsync/WriteRecoveryAsync), WorkspaceFile, IRecoveryStore (ReadManifestAsync/ReadBlobAsync/CommitAsync), FileRecoveryStore, RecoveryWrite, RecoveryBlob, RecoveryKeys, optional ISidecarStorage.

csharpUsage
using LightSpace.Storage;

IRecoveryStore store = new FileRecoveryStore(Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyApp", "recovery"));
// manifestJson must be a valid recovery manifest describing these sources.
byte[] original = File.ReadAllBytes("mountains.jpg");
string key = RecoveryKeys.Hash(original);
await store.CommitAsync(new RecoveryWrite(manifestJson, [key], [new RecoveryBlob(key, original)]));
string? manifest = await store.ReadManifestAsync();
byte[]? restored = await store.ReadBlobAsync(key);

Normally RecoveryPersistence builds these writes. Arbitrary text is not a valid manifest.

LightSpace.Catalog

I/ONo UI depsnet10.0v0.4.1-alpha.191 downloads

Validated serialization, migration, search/filter/sort, copy families, manifests and XMP. Schema 6 accepts catalogs 1–5 and hydrates sources from validated masters. Native XMP retains processing, not family identity. Depends on Core; no UI.

bashInstall
dotnet add package LightSpace.Catalog --prerelease
Depends onCoreUsed byEditing

Basic usage

Key types: CatalogSerializer (Serialize/Deserialize/Validate/SerializeSettings/DeserializeSettings), VirtualCopyCatalog, PhotoQuery, PhotoSort, PhotoKindFilter, XmpSidecar, XmpImportResult, XmpExportResult, RecoveryManifest, RecoverySourceReference.

csharpUsage
using LightSpace.Catalog;
using LightSpace.Core;

CatalogDocument catalog = CatalogSerializer.Deserialize(File.ReadAllText("catalog.json"));
var picks = new PhotoQuery(Text: "alps", MinimumRating: 3,
    Flag: PhotoFlag.Pick, Sort: PhotoSort.Rating).Execute(catalog);
var copies = new PhotoQuery { Kind = PhotoKindFilter.VirtualCopies }.Execute(catalog);
if (picks.Count > 0)
{
    var photo = picks[0];
    XmpImportResult imported = XmpSidecar.Import(File.ReadAllText("IMG_0042.xmp"), photo.State);
    foreach (var warning in imported.Warnings) Console.WriteLine(warning);
    // Interactive hosts should present warnings and apply through EditorSession.
    photo.State = imported.State;
    File.WriteAllText("IMG_0042.xmp", XmpSidecar.Export(photo.State).Xml);
}
File.WriteAllText("catalog.json", CatalogSerializer.Serialize(catalog));

LightSpace.Imaging

I/OSkiaSharpnet10.0v0.4.1-alpha.180 downloads

JPEG, PNG, WebP, BMP and first-GIF-frame decoding, EXIF orientation, downscaled sRGB previews and procedural landscapes. Limits: 64 MiB encoded, 100 megapixels decoded. Depends on Core/SkiaSharp; no UI.

bashInstall
dotnet add package LightSpace.Imaging --prerelease
Depends onCoreUsed byRendering.Skia

Basic usage

Key types: PhotoCodec.Import(name, bytes), PhotoCodec.Decode(bytes, maxDimension), SamplePhotos.CreateCatalog(), SamplePhotos.Create(seed).

csharpUsage
using LightSpace.Core;
using LightSpace.Imaging;
using SkiaSharp;

PhotoDocument photo = PhotoCodec.Import("mountains.jpg", File.ReadAllBytes("mountains.jpg"));
using SKImage preview = PhotoCodec.Decode(photo.Original, maxDimension: 1024);
CatalogDocument samples = SamplePhotos.CreateCatalog();

LightSpace.Rendering.Skia

RenderingSkiaSharpnet10.0v0.4.1-alpha.171 downloads

Development, curves, grading, masks, clones and color ranges use SkSL effects. Optical presentation composes with development; projective geometry is a draw matrix. Backend execution is host-dependent GPU/software. Histograms, Auto tone, bounded sampling and export are included. Matching immutable source arrays share decoding independently of processing. Depends on Imaging; no UI framework.

bashInstall
dotnet add package LightSpace.Rendering.Skia --prerelease
Depends onImagingUsed byControls

Basic usage

Key types: PhotoRenderer (Prepare/Draw/CreateShader/Export/CalculateHistogram/Auto/SampleSource/ReleasePhoto/Statistics/Presentation), Histogram, PhotoTransform, GeometryMapping, ClippingIndicators, ToneLookupCache, BrushCoverageCache, RendererStatistics with its six-field API plus CachedSources/SharedSourceHits.

csharpUsage
using LightSpace.Core;
using LightSpace.Editing;
using LightSpace.Imaging;
using LightSpace.Rendering.Skia;
using SkiaSharp;

var photo = PhotoCodec.Import("architecture.jpg", File.ReadAllBytes("architecture.jpg"));
var session = new EditorSession(new CatalogDocument { Photos = [photo], ActivePhoto = photo.Id });
session.Edit("Correct perspective and contrast", state => state with
{
    Develop = state.Develop with
    {
        Exposure = .3f,
        Channels = new ChannelCurves
        {
            Master = new PointCurve { Points = [new(0, 0), new(.25f, .18f), new(.75f, .82f), new(1, 1)] }
        }
    },
    Geometry = new GeometrySettings { Vertical = -18, Rotate = 1.5f, ConstrainCrop = true },
    Optics = new LensCorrectionSettings(Distortion: 12, Vignetting: 8)
});
using var renderer = new PhotoRenderer();
File.WriteAllBytes("architecture-edited.jpg",
    renderer.Export(photo, SKEncodedImageFormat.Jpeg, quality: 92, maxDimension: 4096));
session.Undo(); // Original bytes remain untouched.

LightSpace.Editing

EngineNo UI depsnet10.0v0.4.1-alpha.174 downloads

EditorSession coalesces completed gestures into transactions. Previews change visible state; commit adds history and cancel restores the opening state. Versions, selective sync, frozen references, copy deltas and committed-revision recovery are included. Depends on Catalog/Storage; no UI.

bashInstall
dotnet add package LightSpace.Editing --prerelease
Depends onCatalogStorageUsed byControls

Basic usage

Key types: EditorSession (Catalog/Active/Selection/Edit/BeginGesture/Preview/CommitGesture/CancelGesture/Undo/Redo/SaveVersion/SyncSelected/ApplySettings/CreateAlbum/Changed), CreateVirtualCopies/RenameVirtualCopy/RemoveVirtualCopies operations, ReferencePhotoSnapshot, SurveySelection, RecoveryPersistence, RecoveryCoordinator.

csharpUsage
using LightSpace.Core;
using LightSpace.Editing;
using LightSpace.Storage;

var session = new EditorSession(catalog);
session.Changed += () => Console.WriteLine($"Revision {session.Revision}");
session.BeginGesture();
foreach (var value in new[] { .1f, .2f, .35f })
    session.Preview(state => state with { Develop = state.Develop with { Exposure = value } });
session.CommitGesture("Exposure");
session.SaveVersion("Bright");
session.SyncSelected(); // All processing; target metadata retained.
session.Undo();
session.SyncSelected(EditSettingsGroup.Light | EditSettingsGroup.ColorGrading);
var copy = session.CreateVirtualCopies([session.Active!.Id]).Single();
session.RenameVirtualCopy(copy.Id, "Warm alternative");
session.Edit("Alternative exposure", state => state with
{
    Develop = state.Develop with { Exposure = .8f }
});
session.RemoveVirtualCopies([copy.Id]);
session.Undo(); // Same copy identity/settings; original never deleted.
var persistence = new RecoveryPersistence(new FileRecoveryStore("recovery"));
using var recovery = RecoveryCoordinator.Incremental(session, persistence, initiallySaved: false);
await recovery.FlushAsync();

LightSpace.Controls

ControlsUno + Skianet10.0-browserwasmnet10.0-desktopv0.4.1-alpha.172 downloads

Original Uno active/reference/Survey views, settings selection, sliders, curves, grading/mixer/range/mask editors, optics/geometry, interactive histogram, panel grips, thumbnails and chrome. Editors expose preview/commit/cancel without owning transaction policy. Brush settings affect new strokes. Depends on Editing/Rendering.Skia; requires Uno's Skia renderer.

bashInstall
dotnet add package LightSpace.Controls --prerelease

Basic usage

Key types: PhotoViewport, ReferencePhotoView, PhotoSurveyView, SurveyCard, SettingsTransferEditor, AdjustmentSlider, PointCurveEditor, ColorGradingEditor, ColorMixerEditor, ColorRangeEditor, MaskSettingsEditor, GeometryEditor, OpticsEditor, BrushSettingsEditor, HistogramView, PanelResizeGrip, PhotoCard, PhotoThumbnail, ThumbnailCache, PanelSection, LightButton, Theme.

csharpUsage
using LightSpace.Controls;
using LightSpace.Editing;
using LightSpace.Rendering.Skia;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

var session = new EditorSession(catalog);
var renderer = new PhotoRenderer();
var viewport = new PhotoViewport(session, renderer);
var exposure = new AdjustmentSlider("Exposure", -5, 5, 0, .01);
exposure.ValueChanged += value => session.Preview(s => s with { Develop = s.Develop with { Exposure = value } });
exposure.ValueCommitted += () => session.CommitGesture("Exposure");
exposure.GestureCanceled += session.CancelGesture;
var curves = new PointCurveEditor { Value = session.Active!.State.Develop.Channels };
curves.Previewed += value => session.Preview(s => s with { Develop = s.Develop with { Channels = value } });
curves.Committed += () => session.CommitGesture("RGB point curve");
curves.Canceled += session.CancelGesture;
var geometry = new GeometryEditor { Value = session.Active.State.Geometry };
geometry.Previewed += value => session.Preview(s => s with { Geometry = value });
geometry.Committed += () => session.CommitGesture("Geometry");
geometry.Canceled += session.CancelGesture;
session.ViewChanged += () =>
{
    if (session.Active is not { } active) return;
    exposure.Value = active.State.Develop.Exposure;
    curves.Value = active.State.Develop.Channels;
    geometry.Value = active.State.Geometry;
};
var inspector = new StackPanel { Width = 300, Children = { exposure, curves, geometry } };
var root = new Grid { Background = Theme.Background };
root.ColumnDefinitions.Add(new ColumnDefinition());
root.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
root.Children.Add(viewport);
var scroll = new ScrollViewer { Content = inspector };
Grid.SetColumn(scroll, 1); root.Children.Add(scroll);
window.Content = root;
window.Closed += (_, _) => { viewport.Dispose(); renderer.Dispose(); };

See reference embedding and Survey for multi-view wiring and releasing session-local processing identities. A standalone view does not dispose its external renderer.

LightSpace.Workbench

WorkbenchUno + Skianet10.0-browserwasmnet10.0-desktopv0.4.1-alpha.185 downloads

Complete workspace control with catalog/grid/filmstrip, editing/optics/geometry, references, Survey, copy manager, settings transfer, tools, versions, dialogs, shortcuts, resizing and recovery. Depends on Controls/Storage; requires Uno. ISidecarStorage is optional.

bashInstall
dotnet add package LightSpace.Workbench --prerelease
Depends onControlsStorage

Basic usage

Key types: StudioView(EditorSession, IWorkspaceStorage, recoveryLoaded, RecoveryPersistence?) exposes Session/Viewport/Recovery/SetGrid/ChooseTool/SetStatus/SaveRecoveryAsync, UnsavedChangesChanged, opt-in DiagnosticsChanged and ProtectExistingRecovery.

csharpUsage
using LightSpace.Editing;
using LightSpace.Imaging;
using LightSpace.Storage;
using LightSpace.Workbench;

IWorkspaceStorage storage = new MyWorkspaceStorage(); // Host picker/export/recovery adapter.
var persistence = storage is IRecoveryStore store ? new RecoveryPersistence(store) : null;
var catalog = persistence is null ? null : await persistence.RestoreAsync();
var studio = new StudioView(new EditorSession(catalog ?? SamplePhotos.CreateCatalog()),
    storage, recoveryLoaded: catalog is not null, persistence: persistence);
window.Content = studio;
window.Closed += (_, _) => studio.Dispose();

Catch restore failures and protect unreadable prior data rather than replacing it automatically; the supplied application host implements that safeguard.

Architecture

From engine to workbench

Layers build bottom‑up: each one depends only on those beneath it.

L1Photo model

Immutable photo state, adjustments, curves, masks and geometry with no Uno dependency.

LightSpace.Core
L2Catalog, imaging and storage

Versioned catalog serialization and XMP, bounded Skia decoding, and platform-independent storage contracts.

LightSpace.CatalogLightSpace.ImagingLightSpace.Storage
L3Editing and rendering

Gesture transactions, undo and versions alongside SkSL development, masks, histogram and export.

LightSpace.EditingLightSpace.Rendering.Skia
L4Uno UI

Original photography controls composed into a reusable workspace for browser and desktop hosts.

LightSpace.ControlsLightSpace.Workbench
Use it in code

Embed, script, extend

csharpEmbed processing
using LightSpace.Core;
using LightSpace.Editing;
using LightSpace.Imaging;
using LightSpace.Rendering.Skia;
using SkiaSharp;

var photo = PhotoCodec.Import("mountains.jpg", File.ReadAllBytes("mountains.jpg"));
var session = new EditorSession(new CatalogDocument { Photos = [photo], ActivePhoto = photo.Id });
session.Edit("Refine contrast", state => state with
{
    Develop = state.Develop with
    {
        Exposure = .3f,
        Channels = new ChannelCurves
        {
            Master = new PointCurve { Points = [new(0, 0), new(.25f, .18f), new(.75f, .82f), new(1, 1)] }
        }
    }
});
using var renderer = new PhotoRenderer();
File.WriteAllBytes("mountains-edited.jpg",
    renderer.Export(photo, SKEncodedImageFormat.Jpeg, quality: 92, maxDimension: 4096));
session.Undo(); // Original encoded bytes are untouched.
bashBuild and run
git clone https://github.com/wieslawsoltes/LightSpace.git
cd LightSpace
python3 scripts/fetch-assets.py
dotnet workload install wasm-tools --skip-manifest-update

# Model, processing, lifetime, recovery, XMP and performance checks
dotnet run --project tests/LightSpace.Engine.Tests -c Release

# Shared native desktop host
dotnet run --project src/LightSpace.App -f net10.0-desktop \
  -p:LightSpaceDesktopOnly=true

# Actual Uno WebAssembly publication
dotnet publish src/LightSpace.App -f net10.0-browserwasm -c Release \
  -o artifacts/publish -p:WasmShellWebAppBasePath=/LightSpace/
python3 scripts/collect-site.py artifacts/publish artifacts/site
python3 scripts/serve-site.py --directory artifacts/site --port 4173

File formats

.jpg / .jpegread/write
.pngread/write
.webpread/write
.bmpimport
.gif (first frame)import
.lightspace (portable catalog)read/write
.xmp sidecarread/write
.zip (selected photos)export

Keyboard essentials

Grid / edit (detail)G / E or D
Crop / masks / brushR / M / B
Brush size[ / ]
Zoom / comparison / originalZ / Y / \
Rating0–5
Pick / reject / clear flagP / X / U
Previous / next photoLeft / Right
Undo / redoCtrl / Cmd Z / Ctrl / Cmd Shift Z
Import / catalog backup / exportCtrl / Cmd I / Ctrl / Cmd S / Ctrl / Cmd E
Select filteredCtrl / Cmd A
Copy / paste settingsCtrl / Cmd C / Ctrl / Cmd V
Cancel gesture or dialogEscape
Build & CI

Continuously verified

Each push runs through GitHub Actions. The public browser build is deployed only from successful pipelines.

Toolchain

.NET SDK10.0.401
Uno SDK6.7.30
Target frameworksnet10.0-browserwasm · net10.0-desktop · net10.0

Third‑party NuGet packages

SkiaSharp3.119.4
bashClone & run the browser build locally
git clone https://github.com/wieslawsoltes/LightSpace.git
cd LightSpace
dotnet workload install wasm-tools
dotnet run --project src/LightSpace.App -f net10.0-browserwasm
Honest boundaries

What it doesn’t claim

The project documents its limits as carefully as its features.

  • Independent early-stage software, not a pixel-identical or feature-complete Lightroom replacement
  • No RAW/DNG/HEIF/TIFF decoding, AI tools, HDR/panorama merging or calibrated camera/lens correction
  • Output is 8-bit sRGB up to an 8192 px long edge; source EXIF/IPTC is not embedded in copies
  • Brush mask coverage is capped at a 1024-pixel long edge, even for export
  • Recovery is local and unencrypted — not cloud sync or a substitute for original-file backups