This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Clojure2D: a JVM-only (no ClojureScript) generative-art / glitch library built directly on Java2D. All math comes from generateme/fastmath (3.x); colors are fastmath vectors (Vec4 RGBA, channels as doubles in [0,255]).
lein test— run all testslein test clojure2d.color-test— one namespacelein test :only clojure2d.color-test/type-conversion-test— one testlein check— compile/reflection check (CI runslein do clean, check, test, under xvfb because windowing code needs a display)lein with-profile +eastwood eastwood— lint with performance/boxed-math linterslein with-profile +dev-codox codox— build API docs intodocs/codox/lein javac— recompile Java sources (also runs automatically via:prep-tasks, after AOT-compilingfastmath.vector)
Java sources are compiled with --release 8; keep Java code Java-8 compatible.
src/clojure2d/protocols.clj— central protocols (ColorProto,PixelsProto,ImageProto,CanvasProto, mouse/key event protos,ShapeProto,RendererProto). Most cross-namespace polymorphism (e.g. "anything convertible to a color", "anything with pixels") goes through these;extendimplementations live in the consuming namespaces.clojure2d.core— image load/save (save-file-typemultimethod per format),Canvas(wrapsBufferedImage+Graphics2D; drawing happens insidewith-canvas/with-oriented-canvasmacros, orientation via theorient-canvasmultimethod), windows/displays (JFrame; display repaints a canvas in its own thread, decoupled from drawing;draw-fnand event handlers receive and return a per-window state instead of using global atoms; events dispatch viakey-pressed/custom-eventetc. multimethods), and session handling (logger, sequential filenames).clojure2d.core.shape— shape construction (moved out of core in 1.5.0-alpha1; breaking).clojure2d.pixels— immutable-ishPixelstype (planar channel arrays) plus filters, compositing, parallel channel ops, log-density renderers. Heavy lifting is in Java:src/clojure2d/java/Pixels.java,filter/*,reconstruction/*(renderer filter kernels),LogDensity/GradientDensity.clojure2d.color(largest ns) — color representations/conversion viato-color, dozens of color spaces (to-XXX/from-XXXraw,to-XXX*/from-XXX*normalized to[0,255]), distances/deltas, palettes and gradients. Subnamespaces:blend,cssgram,munsell,rgb,whitepoints.clojure2d.extra.*— higher-level effects:glitch,overlays,segmentation,signal(sonification, usesjava/signal/Converter.java),utils(visualizers),raymarching(abandoned).
These are data, not code, under resources/:
resources/palettes/*.ednandresources/gradients/*.edn— one file per source/package, each a map of namespaced keywords (e.g.#:MetBrewer{:Signac [...]}). A keyword's namespace selects the file to lazily load (get-palette-or-gradientincolor.clj); integer keys go tocolourlovers. Gradient entries are either a color vector or{:c colors :p positions}.all-palettes.edn/all-gradients.edn— index of all available names; must be updated when adding entries, otherwisepalette/gradientrandom selection and listings won't see them.- Non-namespaced gradients (
:rainbow1,:iq-1,:cubehelix, ...) are defined in code (basic-gradients-delayincolor.clj). resources/color_presets.edn— named colors;resources/color/*.edn— illuminants, white points, CMFs, D-series basis (regenerated byutils/whitepoints/gen_resources.py; white point value changes need explicit approval because they ripple intorgb/rgbs), Munsell renotation data (utils/munsell/), blackbody color table oftemperature(blackbody_srgb.edn,utils/color/gen_temperature.py).utils/palettes/is a separate Lein project that generates these EDN files (from paletteer, matplotlib, thi.ng, colourlovers, etc.) byspit-ing intoresources/— run it from the repo root context so relative paths resolve.docs/static/{palettes,gradients}/*.htmlare generated preview pages committed alongside new palettes/gradients.
- Docstrings use
:metadoc/categoriesmetadata to group functions in generated docs; keep it on new public vars in the main namespaces. metadoc/clojure2d/*_example.clj— examples attached to docs (metadoc).notebooks/color.clj+dev/clerk/— Clerk notebook for color documentation (:devprofile adds Clerk and Clay).generateme/— personal sketches, not part of the library.
resources/clj-kondo.exports/clojure2d/clojure2d/ ships config and macroexpand hooks for library users (with-oriented-canvas, with-oriented-canvas->, make-set-color-fn, whitepoints/gen-whitepoints). Update the hooks when changing or adding such macros.
resources/— datasets used by the library at runtime: external data and anything precalculated (lookup tables). Loaded lazily (delay).test/resources/— oracle/reference data for tests only; must not ship in the package (as in fastmath). On the classpath via the:devprofile's:resource-pathsinproject.clj.utils/— every generator script: dataset converters and oracle-value generators. Current oracle generators:utils/munsell/,utils/whitepoints/,utils/rgb/,utils/blend/,utils/color/(each with a README).- Tests compare against independent oracles (colour/Python, R, Julia, mpmath) and are constructed as in fastmath (see the
fastmath-testingskill); shared predicates (rel-close?,throws-data?) live intest/clojure2d/test_predicates.clj. colour-science is not always the primary source (e.g. its CMCCAT97 matrix is a misprint, its D-series tables stop at 780 nm); see the vault noteTopics/Clojure2D/Colour Oracle Pitfalls. - Oracle toolkits (installing any needed packages is allowed):
Rscript— R packages/home/ts/.juliaup/bin/julia— Julia packagesuvin/home/ts/penv(venv/home/ts/penv/.venv) — Python packagesoctave— Octave/MATLAB packages
- Record step notes in the Obsidian vault (
/home/ts/notes) underTopics/Clojure2D/(asTopics/Fastmath/for fastmath).
- Performance-critical code uses primitive type hints (
^double,^long,^Vec4) and avoids reflection/boxed math; eastwood's:boxed-mathlinter is enabled. - Record user-visible changes in
CHANGELOG.md(keepachangelog style; mark breaking changes with[breaking]).
- Boolean options use Clojure truthiness; don't validate them. An
:rngoption is always an RNG object, never a seed. - Functional style (seqs,
map,reduce) is for code off the hot path; on hot paths imperative code withdouble-arraybuffers is fine — measure rather than assume. ex-datamay carry small values (option name, offending value, bounds), never big sequences (whole inputs/results).- In computational code call
fastmath.core(m/abs,m/hypot,m/nan?, ...) instead ofMath/.../Double/isNaN; useArray/aget/Array/aseton arrays; reusem/MACHINE-EPSILON*,m/TWO_PIetc. instead of literals or constant formulas (otherwise a private^:const). - When clj-kondo reports a type mismatch on
m/+,m/-, ... with longs, usem/long-add,m/long-sub, ...; don't add clj-kondo ignores or wrapper fns. - Hint parameters (
^double x) instead of coercing inside the body;m/ops coerce themselves.(double ...)only for boxed values from user input or collections. If a primitive-hinted fn would need more than 4 args, pass adeftype/Vec2-Vec4/state array instead of dropping hints. - Return fixed groups of numbers as
Vec2/Vec3/Vec4, not double arrays; read deftype fields as(.field x); don't define the same helper twice. - Initialise long loop vars explicitly:
(loop [i (long 0)] ...).