🌈️ apply a wallpaper based on the weather outside
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: algorithm improvements

- use zenith and nadir to calculate max and min angles instead of 90°
- map hue along quintic curve
- adjust hue to use correct sunrise altitude -0.833 instead of 0

Angel Wang (Jun 21, 2026, 12:52 PM -0600) 45ff987e e14c9ae5

+27 -23
+2 -2
deno.json
··· 2 2 "tasks": { 3 3 "compile-analyze": "deno compile --output build/analyze -A analyze/index.ts", 4 4 "compile-apply": "deno compile --output build/apply -A apply/index.ts", 5 - "analyze": "deno run analyze/index.ts", 6 - "apply": "deno run apply/index.ts" 5 + "analyze": "deno run -A analyze/index.ts", 6 + "apply": "deno run -A apply/index.ts" 7 7 }, 8 8 "imports": { 9 9 "@std/assert": "jsr:@std/assert@1"
+4 -12
utils.ts
··· 56 56 return ((number - min) / (max - min)) * (newMax - newMin) + newMin 57 57 } 58 58 59 - export function mapEaseInExpo(number: number, min: number, max: number, newMin: number, newMax: number) { 60 - return easeInExpo((number - min) / (max - min)) * (newMax - newMin) + newMin 59 + function easeOutQuint(x: number): number { 60 + return 1 - Math.pow(1 - x, 5) 61 61 } 62 62 63 - export function mapEaseOutExpo(number: number, min: number, max: number, newMin: number, newMax: number) { 64 - return easeOutExpo((number - min) / (max - min)) * (newMax - newMin) + newMin 65 - } 66 - 67 - function easeInExpo(x: number): number { 68 - return x === 0 ? 0 : Math.pow(2, 10 * x - 10) 69 - } 70 - 71 - function easeOutExpo(x: number): number { 72 - return x === 1 ? 1 : 1 - Math.pow(2, -10 * x) 63 + export function mapEaseOutQuint(number: number, min: number, max: number, newMin: number, newMax: number) { 64 + return easeOutQuint((number - min) / (max - min)) * (newMax - newMin) + newMin 73 65 }
+21 -9
apply/index.ts
··· 1 1 #!/usr/bin/env zx 2 2 3 3 import { ImageAnalysisData } from "../analyze/analysis.ts" 4 - import { cacheDir, configDir, loadConfig, map, mapEaseInExpo, mapEaseOutExpo } from "../utils.ts" 4 + import { cacheDir, configDir, loadConfig, map, mapEaseOutQuint } from "../utils.ts" 5 5 import { ApplicationConfig, defaultConfig, findMatchingImages, getOpenMeteoData } from "./application.ts" 6 6 import * as SunCalc from "suncalc" 7 7 import "zx/globals" ··· 23 23 config.longitude, 24 24 config.weatherModel, 25 25 ) 26 - const sunCalcData = SunCalc.getPosition( 26 + const currentSunData = SunCalc.getPosition( 27 27 new Date(), 28 + config.latitude, 29 + config.longitude, 30 + ) 31 + const zenithSunData = SunCalc.getPosition( 32 + SunCalc.getTimes(new Date(), config.latitude, config.longitude).solarNoon, 33 + config.latitude, 34 + config.longitude, 35 + ) 36 + const nadirSunData = SunCalc.getPosition( 37 + SunCalc.getTimes(new Date(), config.latitude, config.longitude).nadir, 28 38 config.latitude, 29 39 config.longitude, 30 40 ) ··· 33 43 console.info( 34 44 `Current shortwave radiation is ${openMeteoData.shortwaveRadiation} W/m²!`, 35 45 ) 36 - console.info(`Current sun altitude is ${sunCalcData.altitude}°!`) 46 + console.info(`Current sun altitude is ${currentSunData.altitude}°!`) 37 47 38 - const hueValue = Number(sunCalcData.altitude) >= 0 39 - ? mapEaseOutExpo(Number(sunCalcData.altitude), 0, 90, 0, 264) 40 - : mapEaseInExpo(Number(sunCalcData.altitude), 0, -90, 360, 264) 48 + const hueValue = Number(currentSunData.altitude) >= -0.833 49 + ? mapEaseOutQuint(Number(currentSunData.altitude), -0.833, zenithSunData.altitude, 0, 264) 50 + : mapEaseOutQuint(Number(currentSunData.altitude), nadirSunData.altitude, -0.833, 264, 360) 51 + 41 52 const chromaValue = map( 42 - Number(openMeteoData.cloudCover), 53 + 100 - Number(openMeteoData.cloudCover), // gotta invert this one 43 54 0, 44 55 100, 45 - config.chromaRange.end, 46 56 config.chromaRange.start, 57 + config.chromaRange.end, 47 58 ) 59 + 48 60 const lightnessValue = map( 49 61 Number(openMeteoData.shortwaveRadiation), 50 62 0, ··· 64 76 65 77 const targetFile = findMatchingImages(imagesData, hueValue, chromaValue, lightnessValue) 66 78 67 - console.info(`Found target image ${targetFile.path}!`) 68 79 console.info(`Setting wallpaper...`) 69 80 await $`eval ${config.applyWallpaperCommand.replace("%s", targetFile.path)}` 81 + console.info(`Success! Enjoy :)`)