Skip to content

Commit 2e43a5a

Browse files
Improve cloud animation: smaller font, faster clouds, wispy shapes
- Reduced font size from 14px to 10px (less distracting) - Faster cloud movement (0.3-0.7 speed vs 0.08-0.2) - Clouds are now wide/flat wispy shapes instead of round droplets - Added wispy tendrils for more cloud-like appearance - Reduced opacity for subtler effect - Simpler dot characters instead of circles Co-authored-by: openhands <openhands@all-hands.dev>
1 parent bbf35c6 commit 2e43a5a

1 file changed

Lines changed: 75 additions & 82 deletions

File tree

themes/cloudascii/static/js/ascii-clouds.js

Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ASCII Cloud Animation - Wavy Pattern with Fluffy Clouds
1+
// ASCII Cloud Animation - Subtle wavy clouds
22
// Inspired by openhands.dev aesthetic
33

44
(function() {
@@ -8,21 +8,18 @@
88
const ctx = canvas.getContext('2d');
99

1010
const config = {
11-
fontSize: 14,
11+
fontSize: 10,
1212
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
13-
// Wavy pattern characters (light to dense)
14-
waveChars: [' ', '.', '·', ':', '∴', '∵', '░'],
15-
// Cloud puff characters (fluffy look)
16-
cloudChars: ['·', '°', '•', '○', '◦', '◌', '●', '◯', '☁'],
13+
// Subtle characters for wispy clouds
14+
cloudChars: ['.', '·', ':', '∙', '°', '˚', '∘'],
1715
colors: {
18-
wave: { r: 63, g: 185, b: 80 }, // Green
19-
cloud: { r: 121, g: 192, b: 255 }, // Cyan
20-
cloudAlt: { r: 163, g: 113, b: 247 }, // Purple
21-
dim: { r: 80, g: 90, b: 100 }
16+
wave: { r: 63, g: 185, b: 80 },
17+
cloud: { r: 121, g: 192, b: 255 },
18+
cloudAlt: { r: 163, g: 113, b: 247 }
2219
},
23-
cloudCount: 8,
24-
waveOpacity: 0.12,
25-
cloudOpacity: 0.35
20+
cloudCount: 10,
21+
waveOpacity: 0.08,
22+
cloudOpacity: 0.18
2623
};
2724

2825
let width, height, cols, rows;
@@ -35,76 +32,81 @@
3532
canvas.width = width;
3633
canvas.height = height;
3734
cols = Math.floor(width / (config.fontSize * 0.6));
38-
rows = Math.floor(height / (config.fontSize * 1.2));
35+
rows = Math.floor(height / config.fontSize);
3936
initClouds();
4037
}
4138

42-
// Simplex-like noise function for organic patterns
39+
// Noise for wavy patterns
4340
function noise(x, y, t) {
4441
return (
45-
Math.sin(x * 0.05 + t) * 0.5 +
46-
Math.sin(y * 0.08 + t * 0.7) * 0.3 +
47-
Math.sin((x + y) * 0.03 + t * 0.5) * 0.4 +
48-
Math.sin(x * 0.12 - t * 0.3) * 0.2 +
49-
Math.cos(y * 0.06 + t * 0.4) * 0.3
50-
) / 1.7;
42+
Math.sin(x * 0.04 + t) * 0.5 +
43+
Math.sin(y * 0.06 + t * 0.7) * 0.3 +
44+
Math.sin((x + y) * 0.025 + t * 0.5) * 0.4 +
45+
Math.cos(y * 0.05 + t * 0.4) * 0.3
46+
) / 1.5;
5147
}
5248

53-
// Fluffy cloud class with multiple "puffs"
49+
// Wispy cloud - horizontal stretched shape
5450
class Cloud {
5551
constructor(startOffscreen = false) {
56-
this.puffs = [];
57-
this.baseX = startOffscreen ? -60 - Math.random() * 40 : Math.random() * cols;
58-
this.baseY = 5 + Math.random() * (rows - 15);
59-
this.speed = 0.08 + Math.random() * 0.12;
52+
this.reset(startOffscreen);
53+
}
54+
55+
reset(startOffscreen = true) {
56+
// Wide, flat cloud shape
57+
this.width = 40 + Math.random() * 60;
58+
this.height = 3 + Math.random() * 4;
59+
this.baseX = startOffscreen ? -this.width - Math.random() * 50 : Math.random() * cols;
60+
this.baseY = 3 + Math.random() * (rows - 10);
61+
this.speed = 0.3 + Math.random() * 0.4; // Faster
6062
this.phase = Math.random() * Math.PI * 2;
6163
this.colorType = Math.random() > 0.5 ? 'cloud' : 'cloudAlt';
62-
63-
// Create multiple overlapping puffs for fluffy look
64-
const puffCount = 4 + Math.floor(Math.random() * 4);
65-
for (let i = 0; i < puffCount; i++) {
66-
this.puffs.push({
67-
offsetX: (Math.random() - 0.3) * 25,
68-
offsetY: (Math.random() - 0.5) * 8,
69-
radiusX: 8 + Math.random() * 12,
70-
radiusY: 4 + Math.random() * 5,
71-
density: 0.5 + Math.random() * 0.4
64+
this.density = 0.4 + Math.random() * 0.3;
65+
// Wispy tendrils
66+
this.tendrils = [];
67+
const tendrilCount = 3 + Math.floor(Math.random() * 4);
68+
for (let i = 0; i < tendrilCount; i++) {
69+
this.tendrils.push({
70+
offsetX: (Math.random() - 0.5) * this.width * 0.8,
71+
offsetY: (Math.random() - 0.5) * this.height,
72+
width: 8 + Math.random() * 15,
73+
height: 1.5 + Math.random() * 2
7274
});
7375
}
7476
}
7577

7678
update() {
7779
this.baseX += this.speed;
78-
79-
// Reset when off screen
80-
if (this.baseX > cols + 60) {
81-
this.baseX = -60;
82-
this.baseY = 5 + Math.random() * (rows - 15);
83-
this.colorType = Math.random() > 0.5 ? 'cloud' : 'cloudAlt';
80+
if (this.baseX > cols + 20) {
81+
this.reset(true);
8482
}
8583
}
8684

8785
getIntensity(px, py) {
8886
let maxIntensity = 0;
87+
const bobY = Math.sin(time * 0.2 + this.phase) * 0.5;
8988

90-
// Gentle vertical bobbing
91-
const bobY = Math.sin(time * 0.3 + this.phase) * 1.5;
89+
// Main cloud body - very wide ellipse
90+
const cx = this.baseX;
91+
const cy = this.baseY + bobY;
92+
const dx = (px - cx) / (this.width / 2);
93+
const dy = (py - cy) / this.height;
94+
const dist = Math.sqrt(dx * dx + dy * dy * 3);
9295

93-
for (const puff of this.puffs) {
94-
const cx = this.baseX + puff.offsetX;
95-
const cy = this.baseY + puff.offsetY + bobY;
96-
97-
const dx = (px - cx) / puff.radiusX;
98-
const dy = (py - cy) / puff.radiusY;
99-
const dist = Math.sqrt(dx * dx + dy * dy);
96+
if (dist < 1.2) {
97+
maxIntensity = Math.max(0, (1 - dist) * this.density);
98+
}
99+
100+
// Wispy tendrils
101+
for (const t of this.tendrils) {
102+
const tcx = this.baseX + t.offsetX;
103+
const tcy = this.baseY + t.offsetY + bobY;
104+
const tdx = (px - tcx) / t.width;
105+
const tdy = (py - tcy) / t.height;
106+
const tdist = Math.sqrt(tdx * tdx + tdy * tdy * 2);
100107

101-
if (dist < 1.3) {
102-
// Soft falloff for fluffy edges
103-
let intensity = Math.max(0, 1 - dist * dist) * puff.density;
104-
105-
// Add subtle noise for organic texture
106-
intensity += noise(px * 0.5, py * 0.5, time * 2) * 0.15;
107-
108+
if (tdist < 1) {
109+
const intensity = Math.max(0, (1 - tdist) * this.density * 0.7);
108110
maxIntensity = Math.max(maxIntensity, intensity);
109111
}
110112
}
@@ -116,24 +118,16 @@
116118
function initClouds() {
117119
clouds = [];
118120
for (let i = 0; i < config.cloudCount; i++) {
119-
clouds.push(new Cloud(i > config.cloudCount / 3));
121+
clouds.push(new Cloud(i > config.cloudCount / 2));
120122
}
121123
}
122124

123-
function getWaveChar(value) {
124-
const idx = Math.floor(Math.abs(value) * (config.waveChars.length - 1));
125-
return config.waveChars[Math.min(idx, config.waveChars.length - 1)];
126-
}
127-
128125
function getCloudChar(intensity) {
129-
if (intensity < 0.15) return config.cloudChars[0];
130-
if (intensity < 0.25) return config.cloudChars[1];
131-
if (intensity < 0.35) return config.cloudChars[2];
132-
if (intensity < 0.45) return config.cloudChars[3];
133-
if (intensity < 0.55) return config.cloudChars[4];
134-
if (intensity < 0.65) return config.cloudChars[5];
135-
if (intensity < 0.8) return config.cloudChars[6];
136-
return config.cloudChars[7];
126+
const idx = Math.min(
127+
Math.floor(intensity * config.cloudChars.length),
128+
config.cloudChars.length - 1
129+
);
130+
return config.cloudChars[idx];
137131
}
138132

139133
function draw() {
@@ -142,14 +136,14 @@
142136
ctx.textBaseline = 'top';
143137

144138
const charWidth = config.fontSize * 0.6;
145-
const lineHeight = config.fontSize * 1.2;
139+
const lineHeight = config.fontSize;
146140

147141
for (let row = 0; row < rows; row++) {
148142
for (let col = 0; col < cols; col++) {
149143
const x = col * charWidth;
150144
const y = row * lineHeight;
151145

152-
// Check clouds first
146+
// Check clouds
153147
let cloudIntensity = 0;
154148
let cloudColor = null;
155149

@@ -161,21 +155,20 @@
161155
}
162156
}
163157

164-
if (cloudIntensity > 0.1) {
165-
// Draw cloud
158+
if (cloudIntensity > 0.05) {
166159
const char = getCloudChar(cloudIntensity);
167160
const c = config.colors[cloudColor];
168-
const alpha = config.cloudOpacity * cloudIntensity;
161+
const alpha = config.cloudOpacity * (0.3 + cloudIntensity * 0.7);
169162
ctx.fillStyle = `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`;
170163
ctx.fillText(char, x, y);
171164
} else {
172-
// Draw wavy background pattern
173-
const waveValue = noise(col, row, time * 0.5);
165+
// Subtle wavy background
166+
const waveValue = noise(col, row, time * 0.3);
174167

175-
if (Math.abs(waveValue) > 0.2) {
176-
const char = getWaveChar(waveValue);
168+
if (Math.abs(waveValue) > 0.35) {
169+
const char = '.';
177170
const c = config.colors.wave;
178-
const alpha = config.waveOpacity * Math.abs(waveValue);
171+
const alpha = config.waveOpacity * Math.abs(waveValue) * 0.5;
179172
ctx.fillStyle = `rgba(${c.r}, ${c.g}, ${c.b}, ${alpha})`;
180173
ctx.fillText(char, x, y);
181174
}

0 commit comments

Comments
 (0)