Skip to content

LightSpace

Photo, Video & Motionv0.5.0-alpha.1 MIT 2

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 907 lines Catalog 324 lines Editing 313 lines Imaging 116 lines Rendering.Skia 650 lines Storage 112 lines Controls 1,683 lines Workbench 1,125 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
907 lines12 files0 refsUsage
I/O 0.4.1-alpha.1

LightSpace.Catalog

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

No UI depsnet10.0
refs → Core
324 lines6 files1 refsUsage
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
313 lines5 files2 refsUsage
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 refsUsage
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
650 lines11 files1 refsUsage
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 refsUsage
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
1,683 lines25 files2 refsUsage
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,125 lines17 files2 refsUsage
Install from NuGet

8 packages on NuGet.org

All eight libraries are MIT-licensed. The badges below show the versions and downloads on NuGet.org, independently of the current source version. The six engine packages (Core, Storage, Catalog, Imaging, Rendering.Skia, Editing) target net10.0 without Uno; Imaging and Rendering.Skia use SkiaSharp 3.119. Controls and Workbench target net10.0-desktop and net10.0-browserwasm with the Uno Skia renderer. Packages are versioned together, with .snupkg symbols and SourceLink. Build artifacts alone do not imply that a new version has been published.

8packages
0.4.1-alpha.1pre-release · Sep 29, 2026
6plain .NET — no UI framework
Newjust published
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 in use where appropriate.

Dispose workspaces and native-resource caches (PhotoRenderer, ThumbnailCache, PhotoViewport, StudioView). Treat snapshot arrays as copy-on-write and originals as read-only. Objects have one logical owner, normally the UI synchronization context. Component API · Recovery invariants

LightSpace.Core

ModelNo UI depsnet10.0v0.4.1-alpha.1

The editing model includes PhotoState, development/crop/optics/geometry, local masks, clone spots, ratings, flags and keywords. Point curves support linear or shape-preserving interpolation; grading, sampled color ranges, brush strokes and built-in presets are UI-independent. Settings normalize values to their supported ranges. No dependencies and no UI.

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

Key types

  • PhotoState, DevelopSettings (17 adjustments and named Get/Set), CropSettings, CloneSpot.
  • GeometrySettings, LensCorrectionSettings, GeometryProjection, ProjectiveTransform, LensMapping, WhiteBalanceEstimator.
  • PointCurve, ChannelCurves, CompiledPointCurve, ColorGradingSettings, GradingTone, ColorBand.
  • LocalMask, ColorRangeSettings, BrushStroke, BrushStrokeBuilder.
  • CatalogDocument, PhotoDocument, Album, NamedVersion, BuiltInPresets.

Basic usage

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);

LightSpace.Storage

ServiceNo UI depsnet10.0v0.4.1-alpha.1

Host contracts for pickers, downloads and recovery. FileRecoveryStore stages immutable originals under SHA-256 keys and atomically replaces the manifest after required sources exist. No project dependencies or UI.

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

Key types

  • IWorkspaceStorage: OpenImagesAsync, OpenCatalogAsync, SaveAsync, ReadRecoveryAsync, WriteRecoveryAsync; WorkspaceFile(Name, Bytes).
  • IRecoveryStore: ReadManifestAsync, ReadBlobAsync, CommitAsync; FileRecoveryStore(directory).
  • RecoveryWrite(Manifest, References, Blobs), RecoveryBlob(Key, Bytes), RecoveryKeys.
  • ISidecarStorage: optional user-authorized XMP picker.

Basic usage

csharpUsage
using LightSpace.Storage;

IRecoveryStore store = new FileRecoveryStore(Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyApp", "recovery"));

// manifestJson must describe these sources using the recovery manifest contract.
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 in LightSpace.Editing builds these writes for the host.

LightSpace.Catalog

I/ONo UI depsnet10.0v0.4.1-alpha.1

Validated catalog serialization, migration, search/filter/sort, recovery manifests and XMP metadata/settings interchange. Current schema 5 accepts catalogs 1–4 with neutral missing fields. The Camera Raw subset is reported explicitly; native extensions retain complete LightSpace state. Depends on Core; no UI.

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

Key types

  • CatalogSerializer: Serialize, Deserialize, Validate, SerializeSettings, DeserializeSettings.
  • PhotoQuery(Text, MinimumRating, Flag, Album, Sort).Execute(catalog) and PhotoSort.
  • XmpSidecar, XmpImportResult, XmpExportResult, RecoveryManifest, RecoverySourceReference.

Basic usage

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);
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);
    // An interactive host should present warnings before applying this state.
    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.1

Bounded decoding for JPEG, PNG, WebP, BMP and GIF sources, EXIF orientation, downscaled sRGB previews and procedural sample landscapes. Limits are 64 MiB encoded and 100 megapixels decoded. Depends on Core and SkiaSharp; no UI.

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

Key types

  • PhotoCodec.Import(name, bytes) validates source metadata and retains original bytes.
  • PhotoCodec.Decode(bytes, maxDimension) returns an oriented sRGB SKImage.
  • SamplePhotos.CreateCatalog() and SamplePhotos.Create(seed) provide generated content.

Basic usage

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.1

Development, curves, grading, masks, clone spots and color ranges run as SkSL runtime effects. A separate optical effect composes with development; projective geometry is a draw matrix. The backend is selected by the host and may be GPU or software. Histograms, Auto settings, source sampling and JPEG/PNG/WebP export are also provided. Depends on Imaging; no UI framework.

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

Key types

  • PhotoRenderer: Draw, CreateShader, Export, CalculateHistogram, Auto, SampleSource, Statistics and Presentation.
  • Histogram, PhotoTransform, GeometryMapping, ClippingIndicators.
  • ToneLookupCache, BrushCoverageCache and their construction/work statistics.

Basic usage

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 encoded bytes are untouched.

LightSpace.Editing

EngineNo UI depsnet10.0v0.4.1-alpha.1

EditorSession makes each completed gesture one transaction. Previews update the visible state without adding history; commit adds a step and cancel restores the opening state. Named versions, selection synchronization, albums and committed-revision recovery are included. Depends on Catalog and Storage; no UI.

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

Key types

  • EditorSession: Catalog, Active, Selection, Edit, BeginGesture/Preview/CommitGesture/CancelGesture, Undo/Redo, SaveVersion, SyncSelected, CreateAlbum, Changed.
  • RecoveryPersistence(IRecoveryStore): Capture, CommitAsync, RestoreAsync.
  • RecoveryCoordinator: FlushAsync, Status and StatusChanged; overlapping flush calls share one writer.

Basic usage

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(); // Includes crop, geometry, optics and masks; retains target metadata.
session.Undo();

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.1

Original dark Uno photography controls include the corrected-coordinate viewport, adjustment sliders, curves, grading/mixer/range/mask editors, optics/geometry, interactive histogram, panel-resize grips, thumbnails and chrome. Editors expose preview/commit/cancel events without owning the host's transaction policy. Brush settings affect new strokes. Depends on Editing and Rendering.Skia; requires the Uno Skia renderer.

bashInstall
dotnet add package LightSpace.Controls --prerelease

Key types

  • PhotoViewport(EditorSession, PhotoRenderer): SetTool, Fit, Compare, Before, Clipping, MaskOverlay, SetActiveMask, BrushSettings.
  • AdjustmentSlider: ValueChanged, ValueCommitted, GestureCanceled.
  • PointCurveEditor, ColorGradingEditor, ColorMixerEditor, ColorRangeEditor, MaskSettingsEditor, GeometryEditor, OpticsEditor: Value and preview/commit/cancel contracts.
  • BrushSettingsEditor, HistogramView, PanelResizeGrip, PhotoCard, PhotoThumbnail, ThumbnailCache, PanelSection, LightButton, Theme.

Basic usage

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;

// Keep values synchronized after undo, cancellation and external edits.
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 });
Grid.SetColumn(inspector, 1);
root.Children.Add(viewport); root.Children.Add(new ScrollViewer { Content = inspector });
Grid.SetColumn(root.Children[1], 1);
window.Content = root;
window.Closed += (_, _) => { viewport.Dispose(); renderer.Dispose(); };

LightSpace.Workbench

WorkbenchUno + Skianet10.0-browserwasmnet10.0-desktopv0.4.1-alpha.1

The complete application workspace as one control: library/grid, filmstrip, interactive histogram, development/optics/geometry inspectors, tools, presets, versions, sidecar/export dialogs, shortcuts, resizable panels and recovery status. Depends on Controls and Storage; requires Uno. ISidecarStorage remains optional.

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

Key types

  • StudioView(EditorSession, IWorkspaceStorage, recoveryLoaded, RecoveryPersistence?): Session, Viewport, Recovery, SetGrid, ChooseTool, SetStatus, SaveRecoveryAsync.
  • UnsavedChangesChanged and opt-in DiagnosticsChanged events.
  • ProtectExistingRecovery preserves unreadable prior data until explicit replacement.

Basic usage

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

IWorkspaceStorage storage = new MyWorkspaceStorage(); // Your picker/export/recovery implementation.
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();

A production host should catch restore failures and protect unreadable existing recovery rather than automatically replacing it; the supplied application host implements that behavior.

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