These instructions apply to the entire repository.
- The canonical fractal implementations must live in the French multilingual source files under
src/*.ml. - Do not introduce a new fractal only in
public/js/renderer.jsunless it is strictly a browser-side drawing helper; the fractal name itself must exist in the French multilingual source. - Prefer French user-facing labels and status text in the UI.
- Keep internal exported ids stable once introduced unless a rename is explicitly requested.
When adding or changing a fractal, update all relevant places together:
- Add or update the function in the appropriate French multilingual source module in
src/:fractales_escape.ml— escape-time (Mandelbrot family, Julia family, Burning Julia, Biomorphe)fractales_variantes.ml— Celtic, Buffalo, Perpendicular variants, Heart, Duckfractales_dynamique.ml— Newton, Phoenix, Lyapunov, attractors, Duffingfractales_ifs.ml— IFS / Barnsley, Sierpinski, Mandelbulb, Vicsek, …fractales_lsystem.ml— L-system geometric curvesfractales_magnetiques.ml— Magnet family, Lambda, Nova magnétiquefractales_lisse.ml— smooth coloring variants (escape-time with μ formula)fractales_orbitrap.ml— orbit trap variants (min-distance encoding)fractales_export.ml— interpolation / export helpers only (no fractal definitions)
- Register the fractal in
src/main.ml. - Update WASM export expectations in
scripts/compile_wasm.ml. - Update integration expectations in
scripts/integration_checks.py. - Wire the fractal into
public/js/renderer.js:- presets
- family/menu entries
- rendering path
- WASM export loading
- source map
- fractal-specific settings in the dedicated
Options spécifiquesUI group when needed - syntax highlighting lists when needed
- and update the relevant extracted browser helper module in
public/js/if the change affects source-panel loading, benchmark UI, bookmarks, export flows, or another split UI concern
- If the change affects documented capabilities, update
README.md.
- Use
POINT_FRACTALSfor density/attractor or point-cloud rendering. - Use
LINE_FRACTALSfor geometric curves and recursive line drawings. - Use scalar/WASM rendering for escape-time style fractals whenever possible.
- Browser-side helpers in
renderer.jsor sibling modules underpublic/js/are acceptable for drawing/export/UI support, but the main fractal definition must still exist in the French multilingual source.
Each POINT fractal requires the following in renderer.js:
- A JS step function
etapeXxx(x, y, z, ...)— pure iteration step, no escape logic. - Optionally a JS projection function
projeterXxx(x, y, z)if the fractal lives in 3D — maps 3D orbit coords to 2D canvas. - A detection boolean
estXxxdeclared at the top of bothrenderPointFractalandremplirFractalePonctuelle. - Step dispatch and projection dispatch in both render loops (live + export). Missing one loop causes a silent discrepancy between on-screen and exported images.
- Initial orbit seed (
x,y,z) chosen so the orbit converges to the attractor, not a degenerate fixed point or fast escape. - Appropriate
pointsTarget,burnIn, andpointsPerFrametuning.
Scalar/WASM escape-time fractals return an iteration count per pixel. They appear as 2D images regardless of how many dimensions the underlying formula uses. To give a spatial/depth impression on the 2D canvas, prefer the density-orbit (POINT_FRACTALS) approach for inherently volumetric fractals. Escape-time is best for flat sets (Mandelbrot, Julia, Magnetic families).
Projecting a 3D orbit onto the canvas without depth cues produces flat-looking output. The renderer implements depth-weighted density for 3D POINT_FRACTALS: each accumulated point contributes a weight proportional to its distance from the viewer (smaller z = closer = higher weight). This makes front-facing surfaces accumulate more density and appear brighter, giving genuine depth shading without modifying the coloring pipeline.
Any new 3D orbit fractal should:
- Use
est3Dflag to opt into depth weighting in theputPointcall. - Choose a projection angle that clearly separates front from back faces.
- Avoid degenerate orbits: if an update rule is
nz = f(x, z) + c_z, verify at least one ofz₀ ≠ 0orc_z ≠ 0; otherwise z stays zero and the 3D formula collapses to 2D. - Guard against orbit escape in iterative maps with growth: add a reset if
|orbit| > thresholdbefore projecting.
Smooth coloring applies the formula μ = iter + 2 − (ln ln |z|² − ln ln 2) / ln 2 at escape
to eliminate iteration banding. Key constraints for new smooth variants:
- The log function is implemented as
log_lisse(x)using iterative halving/doubling to [1, 2) followed by a 4-term atanh series. Do not call any externalmathmodule inside WAT-compiled code. - Check
ln_r2 > 0before the second log call to avoidlog(0)at the escape boundary. - Clamp
muto[0, max_iter]before returning. - 4-parameter variants (e.g.
julia_lisse(zx, zy, c_re, c_im, max_iter)) follow the same dispatch convention asjulia— they readparams.juliaCre/params.juliaCimin the renderer.
Orbit trap fractals record the minimum distance from the orbit to a geometric shape and return
max_iter / (1 + dist_min × scale). This encodes coloring as a single float in [0, max_iter]
compatible with the existing palette pipeline — no special renderer branch is needed.
Guidelines:
- Choose
scaleso that the transition from bright to dark spans a visually useful range (typicallyscale≈ 10–50 for unit-radius shapes). - 4-parameter orbit trap variants follow the julia dispatch convention (same as smooth variants).
- Orbit trap functions are escape-time fractals, not
POINT_FRACTALS; classify them inwasmFunctionsonly (not inPOINT_FRACTALSorLINE_FRACTALS).
renderer-export.js uses a mock canvas context to capture path commands from
dessinerFractaleLineaire, then emits an SVG <path> element. When adding a new L-system
fractal:
- Add it to
LINE_FRACTALSso the SVG export button becomes visible. - Ensure
dessinerFractaleLineairedispatches on its name. - The mock context captures
moveTo/lineTo; make sure your drawing function does not rely onarc,fillRect, or other non-path primitives (those are silently ignored in the mock).
Fractal-only controls must live in the dedicated Options spécifiques group in the footer, not
mixed permanently into the global controls row. This group is configurable and may host settings
for any fractal family, not only Julia.
Guidelines:
- Keep global controls global: family, fractal, iterations, palette, reset, signet, export.
- Show the
Options spécifiquesgroup only when the active fractal actually uses at least one dedicated setting. - Group each setting family in its own sub-block (for example
#julia-c-controls,#multibrot-power-group) so multiple fractal-specific settings can coexist cleanly. - When adding a new per-fractal parameter, update both the DOM visibility logic in
renderer.jsand the relevant state sync paths in the extracted UI modules (initial selection, bookmark restore, export capture if relevant). - Reuse existing renderer params when possible (
params.juliaCre,params.juliaCim,params.multibrotPower) and introduce new stable param ids only when necessary.
mettreAJourOptionsSpecifiques() shows #julia-c-controls whenever the active fractal belongs to
the Julia-type family: julia, burning_julia, julia_lisse, julia_piege_cercle.
Add any new 4-parameter fractal (zx, zy, c_re, c_im, max_iter) to this list so its
controls appear in Options spécifiques.
#multibrot-power-group belongs to the same configurable Options spécifiques system and should
only be visible for fractals that actually use params.multibrotPower. Do not leave Puissance
visible for unrelated fractals.
renderer-bookmarks.js persists view state arrays in localStorage
under the key "fractales_signets". Entries store the captured view object used by the UI
(fractal, palette, iterations, optional 3D view, and view coordinates when applicable).
No backend required — entirely browser-side.
PALETTES in renderer.js drives all coloring. Each palette has fond (background), interieur (interior / max-iter), and stops[] (RGB gradient stops). When adding a palette, also add an <option> in index.html. Aim for wide hue range and pure-black backgrounds for maximum contrast.
Run these checks after meaningful changes:
node --check public\js\renderer.js
node --check public\js\renderer-source-panel.js
node --check public\js\renderer-bookmarks.js
node --check public\js\renderer-export.js
python scripts\compile_wasm.py
python scripts\integration_checks.py
python scripts\ui_smoke_checks.pyRebuild order matters: compile_wasm.py regenerates public/main_wasm_bundle.ml from src/*.ml, overwriting any manual edits to the bundle. Always edit src/fractales_ifs.ml (and sibling modules), never public/main_wasm_bundle.ml directly.
scripts/integration_checks.py enforces that every fractal registered in src/main.ml appears in all of:
REQUIRED_EXPORTSinintegration_checks.pyexports_requisesinscripts/compile_wasm.mlFRACTAL_FAMILIESinrenderer.jsVIEW_PRESETSinrenderer.jsFRACTAL_SOURCE_MAPinrenderer.jswasmFunctionsmapping (thetypeof exports.x === "function"pattern) inrenderer.js- Classified for rendering: either in
POINT_FRACTALS,LINE_FRACTALS, or inwasmFunctions
The integration checks intentionally read those registries from renderer.js, so keep the
canonical fractal metadata there even if related UI behavior is extracted into sibling modules.
POINT_FRACTALS still need a wasmFunctions entry (the WASM function is compiled for language-showcase purposes even if rendering uses JS step functions).
- Prefer ASCII in code unless the file already uses accented French text.
- Keep changes consistent with the repository’s existing French terminology.
public/main.mlis overwritten bycompile_wasm.py(copied fromsrc/main.ml). Editsrc/main.ml.