Vibe Coding with Claude

Building a Window Shade Timer,
Turn by Turn

A real conversation in which a Python spreadsheet, solar geometry, and two iterations of production code emerged from plain English requests — no spec, no plan, just dialogue.

15 turns Claude Sonnet 4.6 2 Python modules · 1 Excel spreadsheet

Phase 1 — The Initial Request

J Jon

Given a location at latitude 40.65 degrees and longitude -75.27 degrees, can you create a table that for each day of the year gives the local time at which the azimuth of the sun is 259 degrees?

C Claude

A quick exploration of the solar geometry, then code that uses the ephem library to compute sun position in 30-second steps for every day of 2026, scanning for azimuth crossings and interpolating to the exact moment.

Python · ephem scan (excerpt)
observer.date = t.strftime('%Y/%m/%d %H:%M:%S')
sun = ephem.Sun(observer)
az = math.degrees(float(sun.az))

if prev_az < target_az <= az:
    frac = (target_az - prev_az) / (az - prev_az)
    exact_utc = prev_t + timedelta(seconds=30 * frac)

Key insight surfaced mid-computation: azimuth 259° is only reachable from late February through mid-October at this latitude — 136 winter days return N/A because the sun sets before swinging that far west.

sun_azimuth_259.xlsx · 365 rows · EST/EDT auto-detected
What just happened Claude wrote, ran, and debugged Python in one turn — hitting an edge case (Feb 27, sun crosses 259° at alt=1.1°, just above the horizon) and fixing the interpolation guard before presenting the file.

Phase 2 — Iterative Refinement

J Jon

Can you replace the N/A entries with the sunset times for each date?

C Claude

Added a get_sunset() helper using ephem.Observer.next_setting(), then wove sunset times into the 136 N/A rows, colour-coded in warm amber so they're visually distinct from the azimuth-crossing rows.

📊 sun_azimuth_259.xlsx · updated · "sunset" note column added
J Jon

I notice you left dashes for 4 dates for the enter/sunrise times; why is that?

C Claude

Diagnosed the bug: on June 8, 9 and July 3, 4, sunrise azimuth is ~58.94° — just below 59° — so the code didn't flag it as "already past the target." But the actual crossing happens at alt≈0°, right at the horizon, which the −2° altitude guard clipped.

Fix: treat any sunrise azimuth within 1° of the target as a "sunrise" entry.

0 remaining dashes · all 365 rows populated
Vibe coding in action Jon spotted the anomaly; Claude diagnosed it, explained it, and fixed it — in one reply, no stack traces shared.

Phase 3 — The Real Use Case Emerges

J Jon

The application is actually determining when the sun stops hitting windows facing southeast (169 degrees on the compass) so I can know when window shades can be opened.

C Claude

The geometry clicked: 169° + 90° = 259°. Once the sun passes that azimuth, it's no longer shining through the window — the table already gives exactly the right answer, just with a newly understood meaning.

J Jon

It would be useful to know when the sun becomes visible through the window in the morning, i.e., when the sun first reaches 59 degrees (169 − 90), or the sunrise time if the sun rises at an angle already greater than 59 degrees.

C Claude

Added a morning scan column. In winter, sunrise is already past 59° so the window gets sun from the moment the sun rises. Around the summer solstice, the sun rises well to the northeast and only swings around to 59° later — on June 21 not until 5:39 AM EDT.

📊 sun_azimuth_259.xlsx · 2 time columns · "Sun Enters" + "Sun Leaves"

Phase 4 — Correction

J Jon

I made a mistake — instead of 59 degrees, the azimuth for sun entry should be 79 degrees (169 − 90). Can you regenerate the spreadsheet using that value?

C Claude

One-line change — TARGET_AZ_AM = 79.0 — and a full regeneration. On June 21 the sun now enters the window at 7:52 AM EDT instead of the earlier (wrong) time. All 365 rows clean, no dashes.

Phase 5 — Going Deeper

J Jon

Can you come up with a mathematical function that will take as inputs a longitude, latitude and azimuth angle and output the local time when the sun will be at the specified azimuth?

C Claude

Derived the closed-form solution using spherical trigonometry. The key relationships:

sin(alt) = sin(φ)·sin(δ) + cos(φ)·cos(δ)·cos(H) sin(A) = −cos(δ)·sin(H) / cos(alt) Eliminating alt → quadratic in cos(H): C·u² + B·u + D = 0 where u = cos(H) C = cos²(δ) − q²·sin²(A) B = −2·p·q·sin²(A) D = sin²(A)·(1−p²) − cos²(δ) Clock time = 12 + H/15 − EoT/60 − lon/15 + UTC_offset

The ± in the quadratic gives two roots — morning and afternoon crossings. Solar declination δ and Equation of Time (EoT) are computed from day-of-year using Spencer (1971) Fourier series.

Phase 6 — Two Python Modules

J Jon

Can you share the Python code that does this even more accurately?

C Claude

Extracted and cleaned the ephem-based core into a standalone module with a clear public API:

sun_azimuth.py · public API
find_azimuth_times(lat, lon, target_az, date)
  → list of {local_time, hour_angle, altitude_deg, period, tz_label}

azimuth_time(lat, lon, target_az, date, period='PM')
  → datetime | None

Accurate to a few arcseconds via VSOP87 planetary theory inside ephem. Timing errors well under a minute.

🐍 sun_azimuth.py
J Jon

Can you create Python code which does the same thing but doesn't use the ephem library? Lower accuracy is OK.

C Claude

Three bugs found and fixed during development of the stdlib-only version:

Bug 1 — Wrong quadratic coefficient signs (C was negated). Bug 2 — Wrong azimuth formula (sin/cos form instead of Meeus tan form). Bug 3 — Wrong longitude correction sign in the time conversion.

Final results vs. ephem reference:

Verification output
2026-06-21  az=259°  PM   4:00 PM EDT   ← ephem: 4:00 PM EDT  ✓
2026-06-21  az= 79°  AM   7:52 AM EDT   ← ephem: 7:52 AM EDT  ✓
2026-03-15  az=259°  PM   6:15 PM EDT   ← ephem: 6:11 PM EDT  (Δ4 min)
2026-10-13  az=259°  PM   6:14 PM EDT   ← ephem: 6:17 PM EDT  (Δ3 min)
🐍 sun_azimuth_noephem.py · stdlib only