This file provides guidance to agents and LLM's when working with code in this repository.
This directory contains a standalone, static HTML experience: an interactive landscape scene with sky, drifting clouds, tall grass, and a running river. The scene exposes two primary calls to action (LinkedIn and GitHub links) and implements a dynamic favicon that reflects user interactions.
There is a single entry point index.html that includes:
<head>with SEO/meta tags, Open Graph data, and JSON-LDPersonschema for Sam Goldfield.- Inline
<style>defining all layout, animation, and visual styling. - Inline
<script>implementing all interactivity (favicon rendering, keyboard/hover/mouse behavior, scene animation).
No bundler, package manager, or external assets are referenced from within this directory; it is served as static HTML.
From the new directory you can either:
- Open
index.htmldirectly in a browser for very fast iteration. - Or, for behavior that more closely matches production hosting, serve it via a simple HTTP server from the parent repo root and navigate to
/new/:- Using Python 3 from the repo root:
python3 -m http.server 8000- Open
http://localhost:8000/new/in your browser.
- Using Python 3 from the repo root:
There are no configured build, lint, or test commands for this directory.
- Standard meta viewport and mobile web app tags.
- Theme and status bar colors tuned to the sky color (
#87CEEB). - Open Graph tags and
descriptionreflecting the interactive landscape and external links. - JSON-LD
Personschema containing name, URL, job title, employer, and social profiles. If personal details change (e.g., job title or employer), keep this JSON-LD block in sync.
- Global reset (
*) removes margins/padding and hides scrollbars viaoverflow: hiddento keep the scene full-viewport. bodyis a full-viewport gradient background: sky → distant landscape → foreground grass.- Cloud system
.cloudelements with::before/::afterpseudo-elements approximate cloud shapes.@keyframes float-cloudmoves clouds horizontally across the viewport while fading them in/out.
- Water / river
#wateris a horizontal band near the bottom of the viewport, with gradient fill mimicking water..water-waveelements animate via@keyframes flowto create moving light streaks.
- Links and reflections
#links-containeris absolutely centered and contains the two main anchors (LinkedIn and GitHub).- Link elements have fade-out animations unless hovered; CSS classes (
hovered,no-animation,faded) control visibility and timing. .reflectionelements mirror each link’s text, vertically flipped with blur and reduced opacity to simulate reflections in the river..hover-zoneinvisible divs extend the hover area vertically above each link, so moving through the sky can trigger link hover behavior.
- Grass system
#grass-containeroverlays the scene and holds multiple.grass-bladeelements.- Each blade is a thin rectangle with a vertical gradient and transform origin at its base to support swaying.
All interaction logic is implemented in one inline <script> block. Key subsystems:
- Uses a 32×32
<canvas>to draw favicon frames, referenced by the existing<link rel="icon">tag. updateFavicon(state, animationFrame)renders different scenes depending onstate:default: sky, small cloud, water, ground, and static grass blades.grass-moving: emphasizes animated grass and a subtle wind indicator.water-ripple: focuses on water with an expanding ripple.link-hover: highlights a glowing dot and simple text lines to indicate active links.
setFaviconState(state, duration)animates the favicon in the given state fordurationms, then returns todefault. It uses an interval to incrementfaviconAnimationFrameand re-render.
When modifying favicon behavior, keep the contract that state is a small set of string identifiers and that unknown states fall back to the default rendering.
- DOM references:
grass-container,clouds-container, and the globalcloudsandgrassBladesarrays. createClouds()- Creates a fixed number of
.cloudelements with randomized sizes, vertical positions, and animation durations/delays. - Appends clouds to
#clouds-containerand registers them viatrackCloud.
- Creates a fixed number of
createGrass()- Computes
numBladesbased on viewport width. - For each blade, randomizes horizontal position, height, base rotation, and z-index, and stores per-blade metadata (
baseRotation,offset) ingrassBlades.
- Computes
If you adjust cloud/grass densities or visuals, preserve the separation between DOM elements and per-element metadata to keep animations manageable.
updateCloudBrightness()- Disabled on very narrow screens to avoid layout/measurement issues.
- For each cloud with a visible bounding box, iterates over visible links in
#links-container. - Computes center–center distance and adds a
brightenedclass to the cloud when within a threshold distance of any visible link.
This system depends on:
cloudsarray containing all active cloud elements.- Visibility of links being encoded via CSS opacity; links with opacity ≤ 0.5 are treated as effectively hidden.
- Uses a
keysdictionary keyed by event.keyvalues. keydownlistener:- Tracks movement keys: arrow keys and WASD.
- On first press of any movement key, calls
setFaviconState('grass-moving', 2000).
keyuplistener:- Clears the corresponding entry in
keys.
- Clears the corresponding entry in
updateGrass()(called on every animation frame):- Computes
moveX/moveYfrom current key state. - Advances
timeand calculates per-bladewindEffect(sinusoidal) andmoveEffect(driven by movement direction). - Updates each blade’s
transformrotation accordingly.
- Computes
If you add new keyboard interactions, be careful not to conflict with the existing movement keys and their use in updateGrass().
createReflections()- For each link in
#links-container:- Creates a
.reflectionelement with matching text and a.hover-zoneelement extending vertically above the link. updatePositions()keeps reflection and hover-zone aligned to the link on resize.
- Creates a
- Hovering either the link or its hover zone:
- Adds
hovered/no-animationclasses, removesfaded, and triggerssetFaviconState('link-hover', 3000).
- Adds
- On hover end, re-applies fading.
- For each link in
This subsystem tightly couples CSS classes (hovered, faded, no-animation) across both the main links and their reflections; if you refactor these names, update both CSS and JS consistently.
- Click handler on
#water:- Computes click coordinates relative to the water element.
- Creates a
.rippleelement at that point, with a fixed size and CSSripple-animationkeyframe driving its expansion and fade out. - Calls
setFaviconState('water-ripple', 1500)to sync the favicon animation with the ripple. - Removes the ripple element after the animation completes.
animate()usesrequestAnimationFrameto continuously:- Call
updateGrass(). - Call
updateCloudBrightness().
- Call
- On initial load, the script calls:
updateFavicon('default')to seed the favicon.createClouds(),createGrass(),createReflections(), and thenanimate().
The main loop is intentionally simple; if you add more per-frame work, consider performance implications on low-power devices.
- If you split CSS or JavaScript out of
index.htmlinto separate files, update this document to describe the new structure and ensure any new build/serve commands are captured in the "How to run" section. - When adding new interaction modes (e.g., additional keyboard shortcuts or mouse gestures), aim to reuse existing patterns:
- Use small, named favicon
statevalues routed throughsetFaviconState. - Keep one primary
requestAnimationFrameloop and extend it rather than introducing multiple, competing loops.
- Use small, named favicon
- Maintain the alignment between visual elements (links, reflections, hover zones, clouds) and their JavaScript controllers by updating CSS class names and selectors in lockstep.