This is the 2D-native counterpart to the 3D instanced-panel array simulator. Instead of orbiting a rendered rooftop, it draws a schematic top-down grid of panels per string plus a live chart of mismatch loss across every shadow position at the current width, depth and string length, with your current position marked.
Each panel's own maximum-power point follows real PV behaviour: short-circuit current scales almost linearly with irradiance G, while open-circuit voltage falls off logarithmically (it barely changes in light shade, then drops fast in deep shade):
I_mpp(G) ≈ I_stc · G
V_mpp(G) ≈ V_stc · (1 + β·ln(G)), β ≈ 0.05
P_panel = I_mpp · V_mpp
Microinverters / DC optimizers give every panel its own MPP tracker, so the array total is simply Σ P_panel — a shaded panel only loses its own power, nothing more.
A single string inverter puts every panel in that row in series on one MPP tracker, so only one current can flow through the whole string. A panel's bypass diode kicks in once its own MPP current falls below ~30% of full sun (G < 0.3), shorting it out of the voltage sum entirely — but the remaining panels are still forced to share the reduced current set by the weakest surviving panel:
I_string = min( I_mpp,i ) over non-bypassed panels i
V_string = Σ V_mpp,i over non-bypassed panels i
P_string = I_string · V_string
Because one dim panel drags every panel behind it in the string down to its own current, a shadow covering even 2–3 of 10 panels can cost a string inverter far more than the shaded panels' own share of the array — the gap between the two live readouts above is exactly that mismatch loss, and the lower chart traces how that loss changes as the shadow sweeps the whole array so you can see it isn't worst at the middle or edges alone. This is the real-world reason rooftop installers add microinverters or per-panel optimizers on roofs with partial shading (chimneys, vent pipes, trees, neighbouring buildings).