{"id":50896470,"url":"https://github.com/kako-jun/aquarelle","last_synced_at":"2026-06-16T00:35:29.314Z","repository":{"id":359691338,"uuid":"1209184830","full_name":"kako-jun/aquarelle","owner":"kako-jun","description":"Rust crate for deterministic watercolor-style soft-bleed orb rendering onto a tiny-skia pixmap, wasm-friendly","archived":false,"fork":false,"pushed_at":"2026-06-10T18:04:05.000Z","size":37,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-16T00:34:52.770Z","etag":null,"topics":["graphics","rendering","rust","tiny-skia","wasm","watercolor"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kako-jun.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["kako-jun"]}},"created_at":"2026-04-13T07:13:29.000Z","updated_at":"2026-06-10T18:03:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kako-jun/aquarelle","commit_stats":null,"previous_names":["kako-jun/aquarelle"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/kako-jun/aquarelle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Faquarelle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Faquarelle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Faquarelle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Faquarelle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kako-jun","download_url":"https://codeload.github.com/kako-jun/aquarelle/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Faquarelle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34386320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphics","rendering","rust","tiny-skia","wasm","watercolor"],"created_at":"2026-06-16T00:35:28.605Z","updated_at":"2026-06-16T00:35:29.305Z","avatar_url":"https://github.com/kako-jun.png","language":"Rust","funding_links":["https://github.com/sponsors/kako-jun"],"categories":[],"sub_categories":[],"readme":"# aquarelle\n\n[![crates.io](https://img.shields.io/crates/v/aquarelle.svg)](https://crates.io/crates/aquarelle)\n[![docs.rs](https://img.shields.io/docsrs/aquarelle)](https://docs.rs/aquarelle)\n[![CI](https://github.com/kako-jun/aquarelle/actions/workflows/ci.yml/badge.svg)](https://github.com/kako-jun/aquarelle/actions/workflows/ci.yml)\n\nWatercolor-style **soft-bleed orb rendering** on a `tiny_skia::Pixmap`.\n\nGiven a center, a radius, an RGB color, and a `u64` seed, `aquarelle`\ncomposites a calm cel-anime night-scene orb onto a pixel buffer you\nalready own. Four orthogonal knobs — `bleed`, `bloom`, `offset`,\n`halo` — let you tune from a flat soft orb to a hazy paper-bleed\nlight source.\n\nOriginally written as the texture set inside the\n[`orber`](https://crates.io/crates/orber) abstract-mood-image\ngenerator; lifted into its own crate so other watercolor / sumi\nrenderers (e.g. `blueprinter`) can share the same engine.\n\n## Install\n\n```toml\n[dependencies]\naquarelle = \"0.2\"\n```\n\n## Example: orb rendering\n\n```rust\nuse aquarelle::{render_aquarelle_orb, AquarelleParams};\nuse tiny_skia::{Color, Pixmap};\n\nlet mut pix = Pixmap::new(128, 128).unwrap();\npix.fill(Color::from_rgba8(0, 0, 0, 255));\n\nrender_aquarelle_orb(\n    \u0026mut pix,\n    (64.0, 64.0),           // center\n    40.0,                   // radius\n    [200, 100, 50],         // sRGB color\n    42,                     // seed (deterministic)\n    AquarelleParams::default(),\n);\n\n// `pix.data()` is now BGRA bytes you can write to PNG, send to\n// WebCodecs, copy to a GPU texture, etc.\n```\n\n## Example: bleed pass over an existing picture (v0.2)\n\nUse `render_aquarelle_bleed_pass` when the pixmap already contains your\nart (e.g. ink strokes from `blueprinter`) and you want a soft halo\nunderneath the existing pixels.\n\n```rust\nuse aquarelle::{render_aquarelle_bleed_pass, AquarelleBleedParams};\nuse tiny_skia::{Color, FillRule, Paint, PathBuilder, Pixmap, Transform};\n\nlet mut pix = Pixmap::new(128, 128).unwrap();\npix.fill(Color::from_rgba8(255, 255, 255, 255));\n\n// Draw a black dot to bleed.\nlet mut paint = Paint::default();\npaint.set_color_rgba8(0, 0, 0, 255);\nlet mut pb = PathBuilder::new();\npb.push_circle(64.0, 64.0, 8.0);\nlet path = pb.finish().unwrap();\npix.fill_path(\u0026path, \u0026paint, FillRule::Winding, Transform::identity(), None);\n\nrender_aquarelle_bleed_pass(\n    \u0026mut pix,\n    AquarelleBleedParams::default(), // radius 3, intensity 0.5, halo 0.3\n    42,\n);\n```\n\nA 3-pass box blur approximates a Gaussian; a faint seed-derived paper\ngrain is multiplied onto the blurred layer; the original picture is\nthen re-composited on top.\n\n## Spiral bleed — にじみ (v0.3)\n\nThe v0.2 pass above is a whole-pixmap box blur. v0.3 adds a second,\n**per-primitive** bleed: the 48-tap golden-angle spiral (`AQUA_BLEED_WGSL`)\ndeveloped and approved in [`orber`](https://github.com/kako-jun/orber) (#239).\nIt spatially averages each primitive's coverage so a sharp shape spreads into\na formless, organic cloud — closer to pigment spreading on wet paper than a\nuniform blur.\n\nIt ships in two matched forms so any renderer can use it:\n\n- **GPU** — `AQUA_BLEED_WGSL` is a WGSL fragment for `orber` and `additive`\n  (both wgpu) to concatenate into their shaders. (They still carry an in-tree\n  copy today; wiring them onto this shared fragment is a later phase.) The host\n  shader must define `TAU`, `hash21`, `clampf`, and `coverage_at` first\n  (signatures are in the fragment header).\n- **CPU** — `aqua_blurred_coverage_cpu` / `aqua_character_cpu` mirror the same\n  math in Rust for `blueprinter` (no GPU) and as a parity oracle.\n\nThis engine carries **にじみ only** (the formless spread). **ぼやけ** (plain\nedge softness) is intentionally *not* here — it stays in each consumer.\nBecause the spread dissolves shapes, readability is the consumer's job:\ncomposite the sharp original on top of the bleed (ink line + watercolor wash).\n\n```rust\nuse aquarelle::{aqua_blurred_coverage_cpu, aqua_character_cpu, SpiralBleedParams};\n\nlet p = SpiralBleedParams { bleed: 1.0, bloom: 0.0, halo: 0.5, offset: 0.3 };\n\nlet blur_px = 12.0 * p.bleed;\nlet (alpha, scale) = aqua_blurred_coverage_cpu(\n    // your silhouette: return (straight_alpha, rgb_scale) at a tap point (x, y)\n    |x, y| { let _ = (x, y); (0.4, 1.0) },\n    (64.0, 64.0), // sample pixel\n    blur_px,\n    0.0,          // per-primitive seed\n    (0.0, 0.0),   // offset bias (0 = symmetric)\n);\nlet rgb = aqua_character_cpu([0.2, 0.5, 0.9], alpha, p.bloom, p.halo);\nlet _ = (scale, rgb);\n```\n\n## The four elements\n\n| Knob | Range | What it does |\n|---|---|---|\n| `bleed` | `0.0 .. 1.0` | Number and size of same-color satellite gradients scattered around the orb (film grain / paper bleed). |\n| `bloom` | `0.0 .. 1.0` | A near-white core inside the inner ~30 % of the radius so the orb reads as a light source. |\n| `offset` | `0.0 .. 1.0` | Decouples the gradient center from the geometric center by up to 25 % of the radius along a seed-derived angle. |\n| `halo` | `0.0 .. 1.0` | Saturation boost on the outer falloff (film halation feel). |\n\n`AquarelleParams::default()` sets every knob to `0.5` for a calm\nmid-strength orb.\n\n## Renderer-agnostic on purpose\n\n`aquarelle` does **not** know what's already on the pixmap, what the\nrest of your scene looks like, or how you intend to encode the\nresult. It composites four watercolor layers onto the buffer you\nhand it; you decide background fill, layout, output format. This\nkeeps the crate usable from CLIs (PNG via `image`), browser\nvisualizers (`OffscreenCanvas` + `wasm-bindgen`), and animation\npipelines (frame-by-frame `Pixmap` reuse) with the same code.\n\n## Determinism\n\nIdentical `(center, radius, color, seed, params)` produce\nbyte-identical pixels. Internal RNG is seeded per call via\n`ChaCha8Rng::seed_from_u64(seed)` and never touches `thread_rng`,\nso animation pipelines that re-render the same orb every frame\nget a stable visual.\n\n## wasm friendliness\n\nThe crate's only dependencies are `tiny-skia`, `palette`, `rand`,\nand `rand_chacha` — all wasm-compatible. It is used in production\nfrom [`orber-wasm`](https://github.com/kako-jun/orber/tree/main/crates/wasm)\non Cloudflare Pages.\n\n## Status\n\n- **v0.2.0** — adds `render_aquarelle_bleed_pass` for bleeding an\n  existing rasterized picture (3-pass box blur Gaussian approximation +\n  halo saturation boost + seed-derived paper grain). The original\n  `render_aquarelle_orb` API is unchanged. See\n  [Issue #2](https://github.com/kako-jun/aquarelle/issues/2).\n- **v0.1.0** — extracted from `orber-core v0.3.x`'s in-tree `aquarelle`\n  module, where it has been in production since 2026-04 (see\n  [`orber` PR #30 / Issue #8](https://github.com/kako-jun/orber/issues/8)).\n  API is stable; the breakout closes orber Issue #10 and unblocks\n  `blueprinter` adopting the same texture set.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkako-jun%2Faquarelle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkako-jun%2Faquarelle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkako-jun%2Faquarelle/lists"}