import { createGardenAudioConfig } from './audio/garden-audio-config'; import { defaultSettings } from './config/default-settings'; import { runtimeControls } from './config/runtime-controls'; import type { GardenAppConfig } from './config/types'; import { defaultVibeId, vibePresets } from './config/vibe-presets'; import { APP_STORAGE_KEYS, DEFAULT_AUDIO_VOLUME } from './consts'; export { normalizeNumberControlValue, normalizeRuntimeSettings, } from './config/normalize-runtime-settings'; export type { GardenAppConfig, GardenRuntimeSettings, NumberControlConfig, } from './config/types'; export const appConfig = { audio: createGardenAudioConfig(), deltaTime: { maxDeltaTimeSeconds: 1 / 30, minDeltaTimeSeconds: 1 / 240, }, exportSnapshot: { bytesPerPixel: 4, filenameExtension: 'png', filenamePrefix: 'fleeting-garden', filenameSuffix: '-snapshot', mimeType: 'image/png', rowAlignmentBytes: 256, }, menuHider: { bottomRevealDistancePx: 96, desktopMediaQuery: '(min-width: 600px) and (hover: hover) and (pointer: fine)', hideDelayMs: 3000, }, pipelines: { common: { noiseChannelSeeds: [0, 1, 2, 3], noiseClearValue: { r: 1, g: 1, b: 1, a: 1 }, noiseDrawInstanceCount: 1, noiseDrawVertexCount: 3, noiseHashMultiplier: 43758.5453123, noiseHashX: 12.9898, noiseHashY: 78.233, noiseTextureFormat: 'r8unorm', noiseTextureSize: 2048, }, brush: { maxLineCount: 240, }, diffusion: { minDiffusionRate: 0.000001, }, eraser: { maxTextureLineCount: 384, }, }, defaultSettings, runtimeSettings: { controls: runtimeControls, }, simulation: { brushEffectFramesPerSecond: 60, clearColor: { r: 0, g: 0, b: 0, a: 0 }, initialAgentCount: 180_000, // How long the source map continues to be diffused after a brush stroke ends. // 600 frames at ~60 FPS ≈ 10 seconds. sourceActiveFramesAfterWrite: 600, intro: { angleJitterRadians: Math.PI * 0.08, angleEaseEnd: 1, angleEaseStart: 0.6, circleMaxSideRatio: 0.46, circleMinSideRatio: 0.32, drawHintDelayMs: 3000, durationSeconds: 4, entryJitterSideRatio: 0.035, fontScaleDown: 0.94, fontFamily: '"Open Sans", sans-serif', initialFontHeightRatio: 0.28, initialFontWidthRatio: 0.19, letterSpacingEm: 0.07, maskAlphaThreshold: 32, maskGradientThreshold: 8, maskSampleDensity: 540, maxHeightRatio: 0.25, maxWidthRatio: 0.76, minEntryJitterPx: 6, minFontSizePx: 18, minTargetJitterPx: 1, pathEasing: 'easeOutQuad' as GardenAppConfig['simulation']['intro']['pathEasing'], pathProgressEpsilon: 0.001, radialJitterRatio: 0.35, radialStartEpsilon: 0.001, resizeMinimumRemainingSeconds: 1.4, resizeSettleMs: 120, targetDelayDistanceMultiplier: 0.12, targetDelayMax: 0.22, targetDelayRandomMultiplier: 0.06, targetJitterSideRatio: 0.0035, title: 'Fleeting', titleColorCutLetters: [2, 5], titleRadiusMultiplier: 1.55, titleStrokeWidthMinPx: 6, titleStrokeWidthRatio: 0.11, verticalAnchor: 0.47, }, introMoveSpeed: 280, stroke: { densityMultiplier: 110, maxAgentCount: 2_400, }, }, storage: { audioMutedKey: APP_STORAGE_KEYS.audioMuted, audioVolumeKey: APP_STORAGE_KEYS.audioVolume, vibeKey: APP_STORAGE_KEYS.vibe, }, toolbar: { eraser: { controlScaleMax: 1.34, controlScaleMin: 0.74, default: 96, max: 240, min: 24, step: 1, }, mirror: { default: 8, fallbackSegmentName: 'slices', max: 12, min: 1, names: { 2: 'halves', 3: 'thirds', 4: 'quarters', 5: 'fifths', 6: 'sixths', 7: 'sevenths', 8: 'eighths', 9: 'ninths', 10: 'tenths', 11: 'elevenths', 12: 'twelfths', }, offLabel: 'Mirror off', step: 1, }, contrast: { backgroundOpacityMax: 0.82, brightLuminanceThreshold: 0.32, brightWeight: 0.65, bytesPerSample: 4, contrastOffset: 0.05, linearChannelBreakpoint: 0.03928, linearChannelDivisor: 12.92, linearChannelGamma: 2.4, linearChannelOffset: 0.055, linearChannelScale: 1.055, lowContrastThreshold: 3, lowContrastWeight: 1.8, luminanceBase: 0.11, luminanceBlueWeight: 0.0722, luminanceGreenWeight: 0.7152, luminanceRange: 0.28, luminanceRedWeight: 0.2126, sampleColumns: 13, sampleIntervalMs: 300, sampleRows: 7, whiteContrastNumerator: 1.05, }, volume: { default: DEFAULT_AUDIO_VOLUME, max: 1, min: 0, step: 0.01, }, }, tuningPane: { showFpsOverlay: import.meta.env.DEV, startHidden: true, title: 'Garden Settings', }, vibes: { defaultVibeId, presets: vibePresets, }, } satisfies GardenAppConfig;