la montaña se suaviza de noche
Llevaba tiempo dándole vueltas a esta. Quería una versión nocturna de la escena de la cabecera, y la montaña de píxeles que funciona bien de día se veía mal en cuanto todo se oscurecía, demasiado tosca contra un cielo que se supone debe sentirse profundo y lejano.
Así que la montaña de fondo se cambia por una versión suavizada de noche, calcada sobre la misma foto del ferri que usé para la versión en píxeles, pero dibujada como una forma suave rellena en vez de píxeles. Todo lo que está más cerca del agua, los pájaros, los peces, el pequeño corredor, se queda en píxeles. Solo el fondo lejano se suaviza. Le da algo de profundidad, lo cercano nítido y tosco, lo lejano difuminado en la oscuridad tal como sería en realidad.
constelaciones reales, no puntos al azar
Podría haber simplemente esparcido estrellas aleatorias y darlo por terminado, de hecho empecé así durante como una hora:
const stars = Array.from({ length: 40 }, () => ({
x: Math.random() * width,
y: Math.random() * height
}));Pero quería que el cielo fuera de verdad un cielo. Busqué dónde están Casiopea y la Osa Mayor respecto a la Estrella Polar y coloqué ambas constelaciones como patrones de puntos reales en vez de ruido aleatorio. Rotan lentamente alrededor de la Estrella Polar con el tiempo, igual que el cielo real, solo que mucho más rápido, para que de verdad se pueda notar mientras carga la página en vez de a lo largo de toda una noche.
Detalle pequeño que nadie tiene por qué notar, pero yo sé que está ahí y con eso me basta.
recordando dónde estaba
Lo último que añadí: la escena ahora recuerda más o menos dónde estaba todo si vuelves dentro de cinco minutos. Posiciones de los pájaros, rotación de las estrellas, todo, guardado en el almacenamiento local con una marca de tiempo:
export const SCENE_MAX_AGE_MS = 5 * 60 * 1000;
export function loadScene<S>(
storage: StorageLike | null,
key: string,
now: number = Date.now(),
maxAgeMs: number = SCENE_MAX_AGE_MS
): StoredScene<S> | null {
if (!storage) return null;
try {
const raw = storage.getItem(key);
if (!raw) return null;
const parsed = JSON.parse(raw);
if (!parsed || parsed.v !== 1 || typeof parsed.savedAt !== 'number') return null;
const age = now - parsed.savedAt;
if (age < 0 || age >= maxAgeMs) return null;
return parsed as StoredScene<S>;
} catch {
return null;
}
}Te vas a leer un artículo y vuelves a la portada, y no se reinicia sin más a una escena aleatoria nueva, sigue casi desde donde se quedó. Pasados cinco minutos lo suelta y lo vuelve a barajar.
Eso es básicamente el rediseño terminado por ahora. Papel verde musgo, una montaña que de verdad parece la montaña, demasiados pájaros, una barra lateral, y un cielo que sabe realmente dónde está la Osa Mayor. Probablemente siga tocando cositas pequeñas, pero esta es la forma que tiene.