Restructure MycoStack roots, add interactive effects & data flows
- Make Commons Stack the primary root of the MycoStack - Reframe P2P Foundation as both foundation and forward-looking knowledge engine producing convivial knowledge packets → open source protocol toolkits for P4P - Add Data Flows section (trust, resources, favors, capital) with rStack/rSpace links - Move Flow Funding to Tools in Active Research with TBFF from rFunds.online - Lighten starting background and reduce chroma for gentler rainbow fade - Add mouse-interactive mycelial canvas (cursor attraction, spore particles, hypha connection lines, ambient glow) - Add rFunds and rStack to network map and footer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
465320623e
commit
e0af4e23ae
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* Scroll-driven dynamic colors (set by JS) */
|
/* Scroll-driven dynamic colors (set by JS) */
|
||||||
--scroll-bg: oklch(0.08 0.02 30);
|
--scroll-bg: oklch(0.18 0.015 30);
|
||||||
--scroll-fg: oklch(0.85 0.03 80);
|
--scroll-fg: oklch(0.88 0.02 80);
|
||||||
--scroll-accent: oklch(0.45 0.12 60);
|
--scroll-accent: oklch(0.50 0.10 60);
|
||||||
--scroll-depth: 0;
|
--scroll-depth: 0;
|
||||||
|
|
||||||
/* Static palette tokens */
|
/* Static palette tokens */
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ const LINKS = [
|
||||||
{ name: "P2P Foundation", url: "https://wiki.p2pfoundation.net/" },
|
{ name: "P2P Foundation", url: "https://wiki.p2pfoundation.net/" },
|
||||||
{ name: "MycoFi", url: "https://mycofi.earth" },
|
{ name: "MycoFi", url: "https://mycofi.earth" },
|
||||||
{ name: "Mycopunk", url: "https://mycopunk.xyz" },
|
{ name: "Mycopunk", url: "https://mycopunk.xyz" },
|
||||||
|
{ name: "rFunds", url: "https://rfunds.online" },
|
||||||
|
{ name: "rStack", url: "https://rstack.org" },
|
||||||
|
{ name: "(You)rSpace", url: "https://yourspace.online" },
|
||||||
{ name: "Undernet", url: "https://undernet.earth" },
|
{ name: "Undernet", url: "https://undernet.earth" },
|
||||||
{ name: "Psilo-Cyber", url: "https://psilo-cyber.net" },
|
{ name: "Psilo-Cyber", url: "https://psilo-cyber.net" },
|
||||||
{ name: "Compost Capitalism", url: "https://compostcapitalism.xyz" },
|
{ name: "Compost Capitalism", url: "https://compostcapitalism.xyz" },
|
||||||
{ name: "Post-Appitalism", url: "https://post-appitalist.app" },
|
{ name: "Post-Appitalism", url: "https://post-appitalist.app" },
|
||||||
{ name: "(You)rSpace", url: "https://yourspace.online" },
|
|
||||||
{ name: "Trippin Balls", url: "https://trippinballs.lol" },
|
{ name: "Trippin Balls", url: "https://trippinballs.lol" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@ export function HeroSection() {
|
||||||
className="emerge-letter text-sm sm:text-base opacity-40 max-w-xl mx-auto"
|
className="emerge-letter text-sm sm:text-base opacity-40 max-w-xl mx-auto"
|
||||||
style={{ animationDelay: "1.5s" }}
|
style={{ animationDelay: "1.5s" }}
|
||||||
>
|
>
|
||||||
Born from the P2P Foundation and the Commons Stack.
|
Rooted in the Commons Stack. Nourished by the P2P Foundation.
|
||||||
Inoculating the age of the P4P (peer-for-peer) movement.
|
Propagating the P4P (peer-for-peer) movement.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Terminal tagline */}
|
{/* Terminal tagline */}
|
||||||
|
|
|
||||||
|
|
@ -16,42 +16,42 @@ export function LegacySection() {
|
||||||
The Roots
|
The Roots
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg sm:text-xl opacity-70 max-w-2xl mx-auto">
|
<p className="text-lg sm:text-xl opacity-70 max-w-2xl mx-auto">
|
||||||
Every network grows from somewhere. Ours grows from the Commons
|
The MycoStack grows from the Commons Stack — its primary root
|
||||||
Stack, the P2P Foundation, and the broader commons movement.
|
system — nourished by the knowledge commons of the P2P
|
||||||
|
Foundation and the broader commons movement.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* P2P Foundation */}
|
{/* Commons Stack - Primary Root */}
|
||||||
<div className="section-reveal max-w-3xl mx-auto space-y-6">
|
<div className="section-reveal max-w-3xl mx-auto space-y-6">
|
||||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
||||||
The P2P Foundation
|
The Commons Stack
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
||||||
<p>
|
<p>
|
||||||
The{" "}
|
The{" "}
|
||||||
<a
|
<a
|
||||||
href="https://wiki.p2pfoundation.net/"
|
href="https://commonsstack.org"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="domain-link"
|
className="domain-link"
|
||||||
>
|
>
|
||||||
P2P Foundation Wiki
|
Commons Stack
|
||||||
</a>
|
</a>{" "}
|
||||||
, started by Michel Bauwens and shaped by
|
is the primary root of the MycoStack. Its mission:{" "}
|
||||||
hundreds of contributors, documents the emerging landscape of
|
<em>fund and govern the commons</em>. Through pioneering work in
|
||||||
peer-to-peer collaboration and commons-based alternatives. Over
|
token engineering, augmented bonding curves, and conviction
|
||||||
25,000 pages of case studies, theoretical frameworks, policy
|
voting, the Commons Stack and its{" "}
|
||||||
proposals, and practical guides — an open knowledge base
|
<strong>Trusted Seed</strong> community developed regenerative
|
||||||
that continues to inform projects worldwide.
|
funding mechanisms for commons infrastructure.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Out of this work came key frameworks:{" "}
|
But tools alone aren’t enough. The Commons Stack taught us
|
||||||
<strong>commons-based peer production</strong>, the{" "}
|
that technology must be wrapped in culture — in shared
|
||||||
<strong>partner state</strong>, and{" "}
|
values, governance practices, and communities of care. The
|
||||||
<strong>cosmo-localism</strong> (“design global, manufacture
|
Trusted Seed wasn’t just a token-holder registry; it was an
|
||||||
local”). These ideas didn’t belong to any one person
|
experiment in building trust at the speed of consensus. From this
|
||||||
— they emerged from a network of researchers, practitioners,
|
root system, the MycoStack grows outward.
|
||||||
and communities building alternatives in the open.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -94,81 +94,133 @@ export function LegacySection() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Commons Stack history */}
|
{/* P2P Foundation - Foundation AND Forward */}
|
||||||
<div
|
<div
|
||||||
className="section-reveal max-w-3xl mx-auto space-y-6"
|
className="section-reveal max-w-3xl mx-auto space-y-6"
|
||||||
style={{ transitionDelay: "0.15s" }}
|
style={{ transitionDelay: "0.12s" }}
|
||||||
>
|
>
|
||||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
||||||
The Commons Stack
|
The P2P Foundation
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
||||||
<p>
|
<p>
|
||||||
The{" "}
|
The{" "}
|
||||||
<a
|
<a
|
||||||
href="https://commonsstack.org"
|
href="https://wiki.p2pfoundation.net/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="domain-link"
|
className="domain-link"
|
||||||
>
|
>
|
||||||
Commons Stack
|
P2P Foundation Wiki
|
||||||
</a>{" "}
|
</a>
|
||||||
was born from this intellectual tradition. Its mission:{" "}
|
, started by Michel Bauwens and shaped by
|
||||||
<em>fund and govern the commons</em>. Through pioneering work in
|
hundreds of contributors, is both the bedrock beneath the
|
||||||
token engineering, augmented bonding curves, and conviction
|
MycoStack and the frontier ahead of it. Over 25,000 pages of
|
||||||
voting, the Commons Stack and its{" "}
|
case studies, theoretical frameworks, policy proposals, and
|
||||||
<strong>Trusted Seed</strong> community developed regenerative
|
practical guides — an open knowledge base that continues
|
||||||
funding mechanisms for commons infrastructure.
|
to inform projects worldwide.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
But tools alone aren’t enough. The Commons Stack taught us
|
Out of this work came key frameworks:{" "}
|
||||||
that technology must be wrapped in culture — in shared
|
<strong>commons-based peer production</strong>, the{" "}
|
||||||
values, governance practices, and communities of care. The
|
<strong>partner state</strong>, and{" "}
|
||||||
Trusted Seed wasn’t just a token-holder registry; it was an
|
<strong>cosmo-localism</strong> (“design global, manufacture
|
||||||
experiment in building trust at the speed of consensus.
|
local”). But the P2P Foundation doesn’t just preserve
|
||||||
|
knowledge — it transforms it. Each iteration of commons
|
||||||
|
practice feeds back into the knowledge base, refining theories
|
||||||
|
into{" "}
|
||||||
|
<strong>convivial knowledge packets</strong>: living documents
|
||||||
|
that distill decades of research and practice into actionable
|
||||||
|
frameworks communities can actually use.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
These knowledge packets become the seeds of{" "}
|
||||||
|
<strong>open source protocol toolkits</strong> — practical
|
||||||
|
governance, funding, and coordination tools that any community
|
||||||
|
can adopt, adapt, and contribute back to. The P2P Foundation as
|
||||||
|
both root system and fruiting body: absorbing nutrients from the
|
||||||
|
ground, pushing spores into the future.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Flow Funding */}
|
{/* Data Flows - New Section */}
|
||||||
<div
|
<div
|
||||||
className="section-reveal max-w-3xl mx-auto space-y-6"
|
className="section-reveal max-w-3xl mx-auto space-y-6"
|
||||||
style={{ transitionDelay: "0.18s" }}
|
style={{ transitionDelay: "0.15s" }}
|
||||||
>
|
>
|
||||||
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
||||||
Flow Funding
|
Data Flows of the Commons
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
||||||
<p>
|
<p>
|
||||||
The Commons Stack pioneered the Augmented Bonding Curve — a
|
The MycoStack recognizes that the dissemination of{" "}
|
||||||
breakthrough in sustainable funding for the commons. Flow Funding
|
<strong>trust</strong>, <strong>resources</strong>,{" "}
|
||||||
is its natural evolution: less mechanism, more ecology. Where the
|
<strong>favors</strong>, and <strong>capital</strong> are all
|
||||||
ABC created a single reservoir, Flow Funding cultivates{" "}
|
data flows — streams that can be made visible, governed
|
||||||
<em>enmeshed ecologies</em> of inter- and intra-organizational
|
collectively, and managed by communities on self-provisioned
|
||||||
flow — resources circulating continuously between nested
|
infrastructure.
|
||||||
communities the way nutrients cycle through a forest floor.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
At the heart of Flow Funding is a threshold-based model rooted in
|
When communities own their own coordination tools, these flows
|
||||||
the concept of <strong>“enoughness”</strong> —
|
stop being opaque transactions mediated by extractive platforms
|
||||||
enabling an economics of sufficiency rather than accumulation. When
|
and become transparent, reciprocal exchanges governed by the
|
||||||
a node in the network has enough, surplus flows onward to where
|
people who participate in them. Trust becomes legible. Resources
|
||||||
it’s needed. No hoarding, no artificial scarcity. Just the
|
find their way to where they’re needed. Favors compound
|
||||||
continuous circulation that living systems have practiced for
|
into mutual aid networks. Capital circulates instead of
|
||||||
billions of years.
|
accumulating.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
</div>
|
||||||
This is economics as ecology: not designing incentives from above,
|
|
||||||
but cultivating the conditions for resources to find their own
|
<div className="glass-card p-6 sm:p-8 space-y-4">
|
||||||
path — rooted, reciprocal, and regenerative by nature.
|
<h4 className="font-serif text-lg font-semibold opacity-70">
|
||||||
|
Community-Owned Infrastructure for Coordination
|
||||||
|
</h4>
|
||||||
|
<div className="grid gap-4 sm:grid-cols-2 font-mono text-sm">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="font-serif text-base font-semibold not-italic">
|
||||||
|
<a
|
||||||
|
href="https://rstack.org"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="domain-link"
|
||||||
|
>
|
||||||
|
rStack
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="opacity-60">
|
||||||
|
Open source infrastructure stack for community coordination
|
||||||
|
— self-hostable, sovereign, interoperable
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="font-serif text-base font-semibold not-italic">
|
||||||
|
<a
|
||||||
|
href="https://yourspace.online"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="domain-link"
|
||||||
|
>
|
||||||
|
(You)rSpace
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div className="opacity-60">
|
||||||
|
Community-owned digital spaces — where coordination
|
||||||
|
happens on infrastructure you control
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm opacity-50 pt-2">
|
||||||
|
When communities own their stack, every data flow becomes a
|
||||||
|
commons resource rather than a corporate asset.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* P4P framework */}
|
{/* P4P Movement */}
|
||||||
<div
|
<div
|
||||||
className="section-reveal max-w-3xl mx-auto space-y-6"
|
className="section-reveal max-w-3xl mx-auto space-y-6"
|
||||||
style={{ transitionDelay: "0.24s" }}
|
style={{ transitionDelay: "0.20s" }}
|
||||||
>
|
>
|
||||||
<h3 className="font-serif text-3xl sm:text-4xl md:text-5xl font-bold text-center">
|
<h3 className="font-serif text-3xl sm:text-4xl md:text-5xl font-bold text-center">
|
||||||
The Birth of the
|
The Birth of the
|
||||||
|
|
@ -186,9 +238,12 @@ export function LegacySection() {
|
||||||
practice.
|
practice.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Where the original Commons Stack built tools, MycoStack grows
|
The P2P Foundation’s convivial knowledge packets become
|
||||||
ecosystems. Where P2P described a relational dynamic, P4P demands
|
the P4P movement’s open source protocol toolkits —
|
||||||
a commitment. The mycelium doesn’t just connect — it
|
governance patterns, funding mechanisms, and coordination
|
||||||
|
primitives packaged as commons resources that any community can
|
||||||
|
deploy. Where P2P described a relational dynamic, P4P demands a
|
||||||
|
commitment. The mycelium doesn’t just connect — it
|
||||||
nourishes.
|
nourishes.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -239,6 +294,81 @@ export function LegacySection() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Tools in Active Research */}
|
||||||
|
<div
|
||||||
|
className="section-reveal max-w-3xl mx-auto space-y-6"
|
||||||
|
style={{ transitionDelay: "0.22s" }}
|
||||||
|
>
|
||||||
|
<h3 className="font-serif text-2xl sm:text-3xl font-semibold">
|
||||||
|
Tools in Active Research
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-4 text-base leading-relaxed opacity-80">
|
||||||
|
<p>
|
||||||
|
<strong>Flow Funding</strong> — the natural evolution of
|
||||||
|
the Commons Stack’s Augmented Bonding Curve. Less
|
||||||
|
mechanism, more ecology. Where the ABC created a single
|
||||||
|
reservoir, Flow Funding cultivates{" "}
|
||||||
|
<em>enmeshed ecologies</em> of inter- and intra-organizational
|
||||||
|
flow — resources circulating continuously between nested
|
||||||
|
communities the way nutrients cycle through a forest floor.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The latest research takes the form of{" "}
|
||||||
|
<strong>Threshold-Based Flow Funding (TBFF)</strong>, being
|
||||||
|
developed at{" "}
|
||||||
|
<a
|
||||||
|
href="https://rfunds.online"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="domain-link"
|
||||||
|
>
|
||||||
|
rFunds.online
|
||||||
|
</a>
|
||||||
|
. TBFF models funding pools as dynamic funnels operating across
|
||||||
|
three zones: an <strong>overflow zone</strong> where excess funds
|
||||||
|
automatically redistribute to connected pools, a{" "}
|
||||||
|
<strong>healthy zone</strong> of full flow and balanced
|
||||||
|
operations, and a <strong>critical zone</strong> where outflow
|
||||||
|
restricts to conservation mode. Funnels connect via overflow and
|
||||||
|
spending edges, creating living networks of resource circulation
|
||||||
|
governed by thresholds of{" "}
|
||||||
|
<strong>“enoughness”</strong> rather than
|
||||||
|
accumulation.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This is economics as ecology: not designing incentives from above,
|
||||||
|
but cultivating the conditions for resources to find their own
|
||||||
|
path — rooted, reciprocal, and regenerative by nature.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="glass-card p-6 space-y-3 font-mono text-sm">
|
||||||
|
<div className="font-serif text-base font-semibold not-italic opacity-70">
|
||||||
|
TBFF Zones
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-2 sm:grid-cols-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="opacity-50">overflow</div>
|
||||||
|
<div className="opacity-70 text-xs">
|
||||||
|
Above MAX — surplus redistributes to connected funnels
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div style={{ color: "var(--mycelium-green)" }}>healthy</div>
|
||||||
|
<div className="opacity-70 text-xs">
|
||||||
|
Normal ops — full flow rate, balanced funding
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div style={{ color: "var(--compost-amber)" }}>critical</div>
|
||||||
|
<div className="opacity-70 text-xs">
|
||||||
|
Below MIN — outflow restricted, conservation mode
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Pillars */}
|
{/* Pillars */}
|
||||||
<div
|
<div
|
||||||
className="section-reveal grid gap-6 sm:grid-cols-2 max-w-3xl mx-auto"
|
className="section-reveal grid gap-6 sm:grid-cols-2 max-w-3xl mx-auto"
|
||||||
|
|
@ -255,7 +385,7 @@ export function LegacySection() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Evolve",
|
title: "Evolve",
|
||||||
text: "Update the theoretical frameworks for current conditions. Bridge Web3 and traditional commons movements. Experiment with emerging technologies in service of collective flourishing.",
|
text: "Update the theoretical frameworks for current conditions. Distill research into convivial knowledge packets. Transform insights into open source protocol toolkits that communities can deploy.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Propagate",
|
title: "Propagate",
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,27 @@ interface Hypha {
|
||||||
branchCount: number
|
branchCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Spore {
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
vx: number
|
||||||
|
vy: number
|
||||||
|
life: number
|
||||||
|
maxLife: number
|
||||||
|
radius: number
|
||||||
|
}
|
||||||
|
|
||||||
export function MycelialCanvas() {
|
export function MycelialCanvas() {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||||
const hyphaeRef = useRef<Hypha[]>([])
|
const hyphaeRef = useRef<Hypha[]>([])
|
||||||
|
const sporesRef = useRef<Spore[]>([])
|
||||||
const frameRef = useRef<number>(0)
|
const frameRef = useRef<number>(0)
|
||||||
const trailCanvasRef = useRef<HTMLCanvasElement | null>(null)
|
const trailCanvasRef = useRef<HTMLCanvasElement | null>(null)
|
||||||
|
const mouseRef = useRef<{ x: number; y: number; active: boolean }>({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
active: false,
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
|
|
@ -50,8 +66,28 @@ export function MycelialCanvas() {
|
||||||
resize()
|
resize()
|
||||||
window.addEventListener("resize", resize)
|
window.addEventListener("resize", resize)
|
||||||
|
|
||||||
const w = () => canvas.width / (Math.min(window.devicePixelRatio || 1, 2))
|
// Mouse/touch tracking for interactive effects
|
||||||
const h = () => canvas.height / (Math.min(window.devicePixelRatio || 1, 2))
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
|
mouseRef.current.x = e.clientX
|
||||||
|
mouseRef.current.y = e.clientY
|
||||||
|
mouseRef.current.active = true
|
||||||
|
}
|
||||||
|
const onTouchMove = (e: TouchEvent) => {
|
||||||
|
if (e.touches.length > 0) {
|
||||||
|
mouseRef.current.x = e.touches[0].clientX
|
||||||
|
mouseRef.current.y = e.touches[0].clientY
|
||||||
|
mouseRef.current.active = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onMouseLeave = () => {
|
||||||
|
mouseRef.current.active = false
|
||||||
|
}
|
||||||
|
window.addEventListener("mousemove", onMouseMove)
|
||||||
|
window.addEventListener("touchmove", onTouchMove, { passive: true })
|
||||||
|
window.addEventListener("mouseleave", onMouseLeave)
|
||||||
|
|
||||||
|
const w = () => canvas.width / Math.min(window.devicePixelRatio || 1, 2)
|
||||||
|
const h = () => canvas.height / Math.min(window.devicePixelRatio || 1, 2)
|
||||||
|
|
||||||
const createHypha = (
|
const createHypha = (
|
||||||
x: number,
|
x: number,
|
||||||
|
|
@ -62,17 +98,27 @@ export function MycelialCanvas() {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
angle,
|
angle,
|
||||||
speed: 1.2 + Math.random() * 0.8 - depth * 0.15,
|
speed: 1.0 + Math.random() * 0.6 - depth * 0.1,
|
||||||
age: 0,
|
age: 0,
|
||||||
maxAge: 200 + Math.random() * 150 - depth * 20,
|
maxAge: 250 + Math.random() * 200 - depth * 15,
|
||||||
parentX: x,
|
parentX: x,
|
||||||
parentY: y,
|
parentY: y,
|
||||||
depth,
|
depth,
|
||||||
branchCount: 0,
|
branchCount: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Seed initial hyphae from bottom
|
const createSpore = (x: number, y: number): Spore => ({
|
||||||
const seedCount = Math.max(3, Math.floor(w() / 250))
|
x,
|
||||||
|
y,
|
||||||
|
vx: (Math.random() - 0.5) * 1.5,
|
||||||
|
vy: (Math.random() - 0.5) * 1.5 - 0.3,
|
||||||
|
life: 0,
|
||||||
|
maxLife: 60 + Math.random() * 80,
|
||||||
|
radius: 1 + Math.random() * 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Seed initial hyphae from bottom and sides
|
||||||
|
const seedCount = Math.max(4, Math.floor(w() / 200))
|
||||||
for (let i = 0; i < seedCount; i++) {
|
for (let i = 0; i < seedCount; i++) {
|
||||||
const x =
|
const x =
|
||||||
(w() / (seedCount + 1)) * (i + 1) + (Math.random() - 0.5) * 80
|
(w() / (seedCount + 1)) * (i + 1) + (Math.random() - 0.5) * 80
|
||||||
|
|
@ -85,16 +131,29 @@ export function MycelialCanvas() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// Seed a couple from sides
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
const fromLeft = Math.random() > 0.5
|
||||||
|
hyphaeRef.current.push(
|
||||||
|
createHypha(
|
||||||
|
fromLeft ? -10 : w() + 10,
|
||||||
|
h() * (0.4 + Math.random() * 0.4),
|
||||||
|
fromLeft ? -0.2 + Math.random() * 0.4 : Math.PI - 0.2 + Math.random() * 0.4,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const getAccentColor = () => {
|
const getAccentColor = () => {
|
||||||
return (
|
return (
|
||||||
getComputedStyle(document.documentElement)
|
getComputedStyle(document.documentElement)
|
||||||
.getPropertyValue("--scroll-accent")
|
.getPropertyValue("--scroll-accent")
|
||||||
.trim() || "oklch(0.55 0.18 155)"
|
.trim() || "oklch(0.55 0.13 155)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastSeed = 0
|
let lastSeed = 0
|
||||||
|
let lastSporeEmit = 0
|
||||||
|
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
const width = w()
|
const width = w()
|
||||||
|
|
@ -104,10 +163,11 @@ export function MycelialCanvas() {
|
||||||
ctx.clearRect(0, 0, width, height)
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
|
||||||
// Fade the trail canvas slowly
|
// Fade the trail canvas slowly
|
||||||
trailCtx.fillStyle = "rgba(10, 8, 5, 0.008)"
|
trailCtx.fillStyle = "rgba(10, 8, 5, 0.006)"
|
||||||
trailCtx.fillRect(0, 0, width, height)
|
trailCtx.fillRect(0, 0, width, height)
|
||||||
|
|
||||||
const accent = getAccentColor()
|
const accent = getAccentColor()
|
||||||
|
const mouse = mouseRef.current
|
||||||
const alive: Hypha[] = []
|
const alive: Hypha[] = []
|
||||||
|
|
||||||
for (const hypha of hyphaeRef.current) {
|
for (const hypha of hyphaeRef.current) {
|
||||||
|
|
@ -122,6 +182,21 @@ export function MycelialCanvas() {
|
||||||
hypha.angle += (Math.random() - 0.5) * 0.12
|
hypha.angle += (Math.random() - 0.5) * 0.12
|
||||||
// Gentle gravitropism (slightly toward vertical)
|
// Gentle gravitropism (slightly toward vertical)
|
||||||
hypha.angle += (-Math.PI / 2 - hypha.angle) * 0.003
|
hypha.angle += (-Math.PI / 2 - hypha.angle) * 0.003
|
||||||
|
|
||||||
|
// Mouse attraction: hyphae gently curve toward cursor
|
||||||
|
if (mouse.active) {
|
||||||
|
const dx = mouse.x - hypha.x
|
||||||
|
const dy = mouse.y - hypha.y
|
||||||
|
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||||
|
if (dist < 300 && dist > 20) {
|
||||||
|
const targetAngle = Math.atan2(dy, dx)
|
||||||
|
let angleDiff = targetAngle - hypha.angle
|
||||||
|
if (angleDiff > Math.PI) angleDiff -= Math.PI * 2
|
||||||
|
if (angleDiff < -Math.PI) angleDiff += Math.PI * 2
|
||||||
|
hypha.angle += angleDiff * 0.015 * (1 - dist / 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hypha.x += Math.cos(hypha.angle) * hypha.speed
|
hypha.x += Math.cos(hypha.angle) * hypha.speed
|
||||||
hypha.y += Math.sin(hypha.angle) * hypha.speed
|
hypha.y += Math.sin(hypha.angle) * hypha.speed
|
||||||
|
|
||||||
|
|
@ -133,9 +208,9 @@ export function MycelialCanvas() {
|
||||||
const ageRatio = hypha.age / hypha.maxAge
|
const ageRatio = hypha.age / hypha.maxAge
|
||||||
const opacity = Math.max(
|
const opacity = Math.max(
|
||||||
0.05,
|
0.05,
|
||||||
(1 - ageRatio * 0.9) * (0.6 - hypha.depth * 0.08)
|
(1 - ageRatio * 0.9) * (0.6 - hypha.depth * 0.06)
|
||||||
)
|
)
|
||||||
const lineWidth = Math.max(0.3, 2.5 - hypha.depth * 0.4 - ageRatio)
|
const lineWidth = Math.max(0.3, 2.5 - hypha.depth * 0.35 - ageRatio)
|
||||||
|
|
||||||
// Draw on trail canvas for persistence
|
// Draw on trail canvas for persistence
|
||||||
trailCtx.strokeStyle = accent.replace(")", ` / ${opacity * 0.5})`)
|
trailCtx.strokeStyle = accent.replace(")", ` / ${opacity * 0.5})`)
|
||||||
|
|
@ -154,6 +229,25 @@ export function MycelialCanvas() {
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw faint connections between nearby hyphae tips
|
||||||
|
if (ageRatio < 0.5 && hypha.depth < 3) {
|
||||||
|
for (const other of alive) {
|
||||||
|
if (other === hypha) continue
|
||||||
|
const ox = other.x - hypha.x
|
||||||
|
const oy = other.y - hypha.y
|
||||||
|
const od = Math.sqrt(ox * ox + oy * oy)
|
||||||
|
if (od < 80 && od > 10) {
|
||||||
|
const connOpacity = 0.06 * (1 - od / 80)
|
||||||
|
ctx.strokeStyle = accent.replace(")", ` / ${connOpacity})`)
|
||||||
|
ctx.lineWidth = 0.5
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(hypha.x, hypha.y)
|
||||||
|
ctx.lineTo(other.x, other.y)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Branching
|
// Branching
|
||||||
if (
|
if (
|
||||||
hypha.age > 25 &&
|
hypha.age > 25 &&
|
||||||
|
|
@ -171,6 +265,65 @@ export function MycelialCanvas() {
|
||||||
alive.push(hypha)
|
alive.push(hypha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spore particles
|
||||||
|
const now = Date.now()
|
||||||
|
// Emit spores near mouse
|
||||||
|
if (mouse.active && now - lastSporeEmit > 150) {
|
||||||
|
lastSporeEmit = now
|
||||||
|
sporesRef.current.push(
|
||||||
|
createSpore(
|
||||||
|
mouse.x + (Math.random() - 0.5) * 40,
|
||||||
|
mouse.y + (Math.random() - 0.5) * 40
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit ambient spores occasionally
|
||||||
|
if (now - lastSporeEmit > 800 && sporesRef.current.length < 30) {
|
||||||
|
lastSporeEmit = now
|
||||||
|
sporesRef.current.push(
|
||||||
|
createSpore(Math.random() * width, Math.random() * height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update and draw spores
|
||||||
|
const aliveSpores: Spore[] = []
|
||||||
|
for (const spore of sporesRef.current) {
|
||||||
|
spore.life++
|
||||||
|
if (spore.life >= spore.maxLife) continue
|
||||||
|
|
||||||
|
spore.x += spore.vx
|
||||||
|
spore.y += spore.vy
|
||||||
|
spore.vx *= 0.98
|
||||||
|
spore.vy *= 0.98
|
||||||
|
|
||||||
|
const lifeRatio = spore.life / spore.maxLife
|
||||||
|
const sporeOpacity = Math.sin(lifeRatio * Math.PI) * 0.3
|
||||||
|
const sporeRadius = spore.radius * (1 - lifeRatio * 0.5)
|
||||||
|
|
||||||
|
ctx.fillStyle = accent.replace(")", ` / ${sporeOpacity})`)
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(spore.x, spore.y, sporeRadius, 0, Math.PI * 2)
|
||||||
|
ctx.fill()
|
||||||
|
|
||||||
|
aliveSpores.push(spore)
|
||||||
|
}
|
||||||
|
sporesRef.current = aliveSpores
|
||||||
|
|
||||||
|
// Mouse glow effect
|
||||||
|
if (mouse.active) {
|
||||||
|
const gradient = ctx.createRadialGradient(
|
||||||
|
mouse.x, mouse.y, 0,
|
||||||
|
mouse.x, mouse.y, 120
|
||||||
|
)
|
||||||
|
gradient.addColorStop(0, accent.replace(")", " / 0.06)"))
|
||||||
|
gradient.addColorStop(1, accent.replace(")", " / 0)"))
|
||||||
|
ctx.fillStyle = gradient
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.arc(mouse.x, mouse.y, 120, 0, Math.PI * 2)
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
|
||||||
// Draw trail canvas behind
|
// Draw trail canvas behind
|
||||||
ctx.drawImage(trailCanvas, 0, 0, width, height)
|
ctx.drawImage(trailCanvas, 0, 0, width, height)
|
||||||
|
|
||||||
|
|
@ -178,8 +331,7 @@ export function MycelialCanvas() {
|
||||||
hyphaeRef.current = alive.length > 500 ? alive.slice(-400) : alive
|
hyphaeRef.current = alive.length > 500 ? alive.slice(-400) : alive
|
||||||
|
|
||||||
// Periodically seed new roots
|
// Periodically seed new roots
|
||||||
const now = Date.now()
|
if (now - lastSeed > 2500 && alive.length < 400) {
|
||||||
if (now - lastSeed > 3000 && alive.length < 350) {
|
|
||||||
lastSeed = now
|
lastSeed = now
|
||||||
const x = Math.random() * width
|
const x = Math.random() * width
|
||||||
hyphaeRef.current.push(
|
hyphaeRef.current.push(
|
||||||
|
|
@ -194,6 +346,9 @@ export function MycelialCanvas() {
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", resize)
|
window.removeEventListener("resize", resize)
|
||||||
|
window.removeEventListener("mousemove", onMouseMove)
|
||||||
|
window.removeEventListener("touchmove", onTouchMove)
|
||||||
|
window.removeEventListener("mouseleave", onMouseLeave)
|
||||||
cancelAnimationFrame(frameRef.current)
|
cancelAnimationFrame(frameRef.current)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -201,8 +356,8 @@ export function MycelialCanvas() {
|
||||||
return (
|
return (
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="fixed inset-0 pointer-events-none"
|
className="fixed inset-0"
|
||||||
style={{ zIndex: 0, opacity: 0.55 }}
|
style={{ zIndex: 0, opacity: 0.6, pointerEvents: "none" }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const NODES: NetworkNode[] = [
|
||||||
{
|
{
|
||||||
name: "Commons Stack",
|
name: "Commons Stack",
|
||||||
domain: "commonsstack.org",
|
domain: "commonsstack.org",
|
||||||
description: "Fund and govern the commons",
|
description: "Fund and govern the commons — primary root",
|
||||||
x: 50,
|
x: 50,
|
||||||
y: 10,
|
y: 10,
|
||||||
},
|
},
|
||||||
|
|
@ -40,57 +40,71 @@ const NODES: NetworkNode[] = [
|
||||||
name: "MycoFi",
|
name: "MycoFi",
|
||||||
domain: "mycofi.earth",
|
domain: "mycofi.earth",
|
||||||
description: "Mycoeconomics & regenerative currencies",
|
description: "Mycoeconomics & regenerative currencies",
|
||||||
x: 25,
|
x: 22,
|
||||||
y: 20,
|
y: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mycopunk",
|
name: "Mycopunk",
|
||||||
domain: "mycopunk.xyz",
|
domain: "mycopunk.xyz",
|
||||||
description: "Building from beneath the surface",
|
description: "Building from beneath the surface",
|
||||||
x: 75,
|
x: 78,
|
||||||
y: 15,
|
y: 15,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "rFunds",
|
||||||
|
domain: "rfunds.online",
|
||||||
|
description: "Threshold-Based Flow Funding research",
|
||||||
|
x: 32,
|
||||||
|
y: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rStack",
|
||||||
|
domain: "rstack.org",
|
||||||
|
description: "Open source community coordination infrastructure",
|
||||||
|
x: 75,
|
||||||
|
y: 58,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Compost Capitalism",
|
name: "Compost Capitalism",
|
||||||
domain: "compostcapitalism.xyz",
|
domain: "compostcapitalism.xyz",
|
||||||
description: "Decomposing extractive systems",
|
description: "Decomposing extractive systems",
|
||||||
x: 12,
|
x: 10,
|
||||||
y: 50,
|
y: 50,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "The Undernet",
|
name: "The Undernet",
|
||||||
domain: "undernet.earth",
|
domain: "undernet.earth",
|
||||||
description: "Community-owned infrastructure",
|
description: "Community-owned infrastructure",
|
||||||
x: 88,
|
x: 90,
|
||||||
y: 45,
|
y: 40,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Post-Appitalism",
|
name: "Post-Appitalism",
|
||||||
domain: "post-appitalist.app",
|
domain: "post-appitalist.app",
|
||||||
description: "Tools beyond extractive platforms",
|
description: "Tools beyond extractive platforms",
|
||||||
x: 22,
|
x: 18,
|
||||||
y: 78,
|
y: 78,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "(You)rSpace",
|
name: "(You)rSpace",
|
||||||
domain: "yourspace.online",
|
domain: "yourspace.online",
|
||||||
description: "Spaces that belong to communities",
|
description: "Community-owned digital spaces",
|
||||||
x: 60,
|
x: 62,
|
||||||
y: 80,
|
y: 78,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Psilo-Cyber",
|
name: "Psilo-Cyber",
|
||||||
domain: "psilo-cyber.net",
|
domain: "psilo-cyber.net",
|
||||||
description: "Encrypted mesh networks",
|
description: "Encrypted mesh networks",
|
||||||
x: 82,
|
x: 85,
|
||||||
y: 72,
|
y: 72,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trippin Balls",
|
name: "Trippin Balls",
|
||||||
domain: "trippinballs.lol",
|
domain: "trippinballs.lol",
|
||||||
description: "Expand your perspective",
|
description: "Expand your perspective",
|
||||||
x: 42,
|
x: 40,
|
||||||
y: 88,
|
y: 90,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -101,9 +115,14 @@ const CONNECTIONS: [string, string][] = [
|
||||||
["mycostack.xyz", "undernet.earth"],
|
["mycostack.xyz", "undernet.earth"],
|
||||||
["mycostack.xyz", "compostcapitalism.xyz"],
|
["mycostack.xyz", "compostcapitalism.xyz"],
|
||||||
["mycostack.xyz", "yourspace.online"],
|
["mycostack.xyz", "yourspace.online"],
|
||||||
|
["mycostack.xyz", "rstack.org"],
|
||||||
["commonsstack.org", "wiki.p2pfoundation.net"],
|
["commonsstack.org", "wiki.p2pfoundation.net"],
|
||||||
["commonsstack.org", "mycofi.earth"],
|
["commonsstack.org", "mycofi.earth"],
|
||||||
["commonsstack.org", "mycopunk.xyz"],
|
["commonsstack.org", "mycopunk.xyz"],
|
||||||
|
["commonsstack.org", "rfunds.online"],
|
||||||
|
["rfunds.online", "mycofi.earth"],
|
||||||
|
["rstack.org", "yourspace.online"],
|
||||||
|
["rstack.org", "undernet.earth"],
|
||||||
["wiki.p2pfoundation.net", "post-appitalist.app"],
|
["wiki.p2pfoundation.net", "post-appitalist.app"],
|
||||||
["mycofi.earth", "mycopunk.xyz"],
|
["mycofi.earth", "mycopunk.xyz"],
|
||||||
["undernet.earth", "psilo-cyber.net"],
|
["undernet.earth", "psilo-cyber.net"],
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@ import { useEffect } from "react"
|
||||||
|
|
||||||
// Color stops: [scrollPos, bg-L, bg-C, bg-H, fg-L, fg-C, fg-H, accent-L, accent-C, accent-H]
|
// Color stops: [scrollPos, bg-L, bg-C, bg-H, fg-L, fg-C, fg-H, accent-L, accent-C, accent-H]
|
||||||
// 8 sections: Hero → Legacy → Compost → Mycelium → Undernet → Anastomosis → Emergence → Network Map
|
// 8 sections: Hero → Legacy → Compost → Mycelium → Undernet → Anastomosis → Emergence → Network Map
|
||||||
|
// Gentler rainbow fade with lighter start, lower chroma for subtlety
|
||||||
const COLOR_STOPS: number[][] = [
|
const COLOR_STOPS: number[][] = [
|
||||||
[0.0, 0.08, 0.02, 30, 0.85, 0.03, 80, 0.45, 0.12, 60 ], // Hero: deep soil
|
[0.0, 0.18, 0.015, 30, 0.88, 0.02, 80, 0.50, 0.10, 60 ], // Hero: warm twilight (lighter)
|
||||||
[0.10, 0.10, 0.03, 35, 0.84, 0.03, 80, 0.50, 0.13, 50 ], // Legacy: dark warm earth
|
[0.10, 0.16, 0.018, 40, 0.86, 0.02, 80, 0.52, 0.10, 50 ], // Legacy: warm earth
|
||||||
[0.20, 0.12, 0.04, 40, 0.82, 0.04, 80, 0.55, 0.15, 45 ], // Compost: earth brown
|
[0.20, 0.15, 0.025, 50, 0.84, 0.03, 75, 0.55, 0.11, 45 ], // Compost: amber earth
|
||||||
[0.35, 0.16, 0.05, 150, 0.88, 0.05, 145, 0.55, 0.18, 155], // Mycelium: forest green
|
[0.35, 0.18, 0.03, 150, 0.88, 0.03, 145, 0.55, 0.13, 155], // Mycelium: forest green
|
||||||
[0.48, 0.22, 0.04, 200, 0.90, 0.03, 200, 0.50, 0.14, 220], // Undernet: deep blue
|
[0.48, 0.22, 0.025, 210, 0.90, 0.02, 200, 0.52, 0.10, 220], // Undernet: deep teal
|
||||||
[0.60, 0.45, 0.06, 140, 0.15, 0.03, 150, 0.65, 0.18, 145], // Anastomosis: transition
|
[0.60, 0.45, 0.03, 160, 0.15, 0.02, 150, 0.60, 0.12, 145], // Anastomosis: transition
|
||||||
[0.78, 0.85, 0.04, 110, 0.15, 0.04, 100, 0.60, 0.16, 90 ], // Emergence: golden light
|
[0.78, 0.82, 0.025, 110, 0.15, 0.03, 100, 0.58, 0.12, 90 ], // Emergence: golden light
|
||||||
[0.92, 0.94, 0.02, 110, 0.12, 0.03, 100, 0.55, 0.18, 155], // Network Map: canopy
|
[0.92, 0.92, 0.015, 120, 0.12, 0.02, 100, 0.55, 0.13, 155], // Network Map: canopy
|
||||||
]
|
]
|
||||||
|
|
||||||
function lerp(a: number, b: number, t: number) {
|
function lerp(a: number, b: number, t: number) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue