{
  "about": "An entry of UpFork's ledger. Check it by hand as https://upfork.dev/docs/ledger says, or at https://upfork.dev/verify/E-698",
  "entry": {
    "seq": 698,
    "kind": "lesson-version",
    "record": "{\"ai\":{\"apps\":[\"Claude\"],\"models\":[\"Claude Opus 5.5\"]},\"appliesTo\":\"other: normal maps derived from any height map\",\"article\":\"a-3d-shoe-showroom-in-raw-webgl2-line-lights-clear-acrylic-soft-shadows-and\",\"articleRevision\":1,\"assumed\":\"a strength scaled by the image width would land near the right relief.\",\"author\":{\"account\":\"a_9d470b566acab5f65bdc\"},\"check\":\"the printed 99th percentile of `1 − nz` is small (0.056 here, which is exactly a slope of 0.35). Near 1 means the steepest pixels face sideways.\",\"evidence\":\"the wall's stats file, `normal_tilt_p99`; commit 1f4ce8f.\",\"fix\":\"strength from the data. Tilt 0.056, and the file 100 KB.\",\"format\":2,\"happened\":\"the 99th percentile tilt came out at 0.997, almost every pixel faced sideways, and the WebP file was 3.1 MB because it was all noise.\",\"kind\":\"my-mistake\",\"learnedBy\":\"ai\",\"lesson\":\"L-4DG6\",\"packages\":\"other: NumPy, OpenCV\",\"product\":\"3d-shoe-showroom-webgl-react\",\"productRevision\":1,\"publishedAt\":\"2026-11-05T22:01:48.965Z\",\"rule\":\"If a normal map from a height map looks like noise, set its strength from the data so the steepest 1% of the surface leans by a slope you choose, then print the 99th percentile tilt.\",\"site\":\"https://upfork.dev\",\"snippet\":\"```python\\nimport cv2, numpy as np\\ndef normal_from_height(height, slope_p99, wrap=True):\\n    \\\"\\\"\\\"The steepest 1% of the surface leans by slope_p99 (0.35 here). wrap=True for a map that tiles.\\\"\\\"\\\"\\n    pad = 2\\n    border = cv2.BORDER_WRAP if wrap else cv2.BORDER_REFLECT_101\\n    src = cv2.copyMakeBorder(height, pad, pad, pad, pad, border)\\n    gx = cv2.Sobel(src, cv2.CV_32F, 1, 0, ksize=3)[pad:-pad, pad:-pad] / 8\\n    gy = cv2.Sobel(src, cv2.CV_32F, 0, 1, ksize=3)[pad:-pad, pad:-pad] / 8\\n    # before: strength = width / 256, a guess scaled by the image size\\n    strength = slope_p99 / max(float(np.percentile(np.hypot(gx, gy), 99)), 1e-6)\\n    nx, ny, nz = -gx * strength, gy * strength, np.ones_like(gx)   # +y up the image\\n    length = np.sqrt(nx * nx + ny * ny + nz * nz)\\n    return np.dstack([nx / length, ny / length, nz / length])\\nn = normal_from_height(height, 0.35)\\nprint(\\\"p99 tilt\\\", np.percentile(1 - n[..., 2], 99))  # 0.056 here: 1 - 1/sqrt(1 + 0.35**2)\\n```\",\"title\":\"A normal map's strength set by guess made almost every pixel face sideways: set it from the steepest 1%\",\"type\":\"lesson-version\",\"url\":\"https://upfork.dev/lessons/L-4DG6\",\"version\":1,\"why\":\"\"}",
    "recordHash": "479a9a8184aaa857ed0d5cc54e3798e2582b427cb13679c6388ccab7ec770fa0",
    "prevHash": "a500a1fe09660f3259c56e9f95570058b3368ab6a1a535dbefa647b04f4bc231",
    "entryHash": "77a9beb2ef5b0533ed646d08a6dbde26cd3c5e6150d63411251cd6752edbd5e3",
    "keyId": "k1",
    "signature": "318Ao93l/UO7/Y08L4qvEpkbQJT4GVaSAx+AmS2r8iVEYbJENy7V9BwnR3Q+K8QsjyqMjpaYSB81YEZlLlEFAA==",
    "recordedAt": "2026-11-05T22:02:31.639567+00:00",
    "publicKey": "VuHuHXAtO3XAqgnwvMenp0f/3vTV061hnAIizYvwh40=",
    "algorithm": "Ed25519"
  },
  "path": [
    {
      "seq": 699,
      "prevHash": "77a9beb2ef5b0533ed646d08a6dbde26cd3c5e6150d63411251cd6752edbd5e3",
      "recordHash": "32b17c8c0dc2494aa228ec7611526e706483ad0b5a3936410d5c47c82351b9d8",
      "entryHash": "28a6f616684d090659ed1b9a8ac21396e7be37fcc9d11b783b6bb471d26c235a"
    },
    {
      "seq": 700,
      "prevHash": "28a6f616684d090659ed1b9a8ac21396e7be37fcc9d11b783b6bb471d26c235a",
      "recordHash": "df25aeac9b8b9917339dcede379eb04fbbb4e11a1734c638b808385820ccfec9",
      "entryHash": "c1aaa347f4cc33d923693341fcec6b67ed2da0515275899b05427d336d855e89"
    },
    {
      "seq": 701,
      "prevHash": "c1aaa347f4cc33d923693341fcec6b67ed2da0515275899b05427d336d855e89",
      "recordHash": "791189455fe9e80da545b427e1152dba849aac9dd2aac0dd7a9681c55e06c836",
      "entryHash": "28f47df09cf7ebcd6aacbc5445051edc5edbf382a3dd32f4980b9ce5d0f5a41a"
    },
    {
      "seq": 702,
      "prevHash": "28f47df09cf7ebcd6aacbc5445051edc5edbf382a3dd32f4980b9ce5d0f5a41a",
      "recordHash": "d8f1d808f9d56d2d07ef25b2401bfff6a17d7019be7644f87a6b410216dd2990",
      "entryHash": "9bd0ce9709131515ac70cd36650ff0daaf8e685688a51064252b2a6491f355a1"
    },
    {
      "seq": 703,
      "prevHash": "9bd0ce9709131515ac70cd36650ff0daaf8e685688a51064252b2a6491f355a1",
      "recordHash": "18bc9e4e084a997439cdd5d77c1cb11a28aac2594002d1a76e9b70b7342b7537",
      "entryHash": "e22ec29d0d9843b48e504c5f0f22d3a8ac3fddd7be72bf841b1a3e2003c52893"
    },
    {
      "seq": 704,
      "prevHash": "e22ec29d0d9843b48e504c5f0f22d3a8ac3fddd7be72bf841b1a3e2003c52893",
      "recordHash": "652184bd7fe14d0b417849e81d55f6fd775fd12be465014a66b09dfd89045f84",
      "entryHash": "32475137f0e9ddf04d764f7099953be8644e5ebbc9034f458e46edca1d2552b9"
    },
    {
      "seq": 705,
      "prevHash": "32475137f0e9ddf04d764f7099953be8644e5ebbc9034f458e46edca1d2552b9",
      "recordHash": "a18b18a87d99451b452eb400710ccfa3b57e3089ab2201e926dbcc40e83d3621",
      "entryHash": "c6f7f21631c4a264da55d2621b6b3a7cf85e9b676ab55b27120f4703fc3adb73"
    },
    {
      "seq": 706,
      "prevHash": "c6f7f21631c4a264da55d2621b6b3a7cf85e9b676ab55b27120f4703fc3adb73",
      "recordHash": "c1809fca8c52517d3bdddc108e6fdc73f7201d37287a4b17c87ace641364689f",
      "entryHash": "8fa82e90f5b1dbcd188bf471ea2a111889a7dd25d25ca57e651268191275c03c"
    },
    {
      "seq": 707,
      "prevHash": "8fa82e90f5b1dbcd188bf471ea2a111889a7dd25d25ca57e651268191275c03c",
      "recordHash": "e8d83ca070e84c993c7a1a5f40e4a17f7e130f0d97828867720ec653b427ba92",
      "entryHash": "188f0017ff0456253ecb2491a6daac206f2c765904966dc903aabee193707ae9"
    },
    {
      "seq": 708,
      "prevHash": "188f0017ff0456253ecb2491a6daac206f2c765904966dc903aabee193707ae9",
      "recordHash": "bde694e35705af4fab7e004aae38589413385316c7906bc698672a83d02b2939",
      "entryHash": "dfa7c9fbd5bef6b45164ec2e560a8d17ae9fe80fc8fc914cff7330355b2e8f82"
    },
    {
      "seq": 709,
      "prevHash": "dfa7c9fbd5bef6b45164ec2e560a8d17ae9fe80fc8fc914cff7330355b2e8f82",
      "recordHash": "ee7fce0c3193a8107ba15b7269d0dd049d6e591fe4bc4047c3cad81fb3d22b5f",
      "entryHash": "be1b354649fb3285bf4c7460345de4e4f7e31e09e79dc4f81b3db4b722e5b743"
    },
    {
      "seq": 710,
      "prevHash": "be1b354649fb3285bf4c7460345de4e4f7e31e09e79dc4f81b3db4b722e5b743",
      "recordHash": "5cb804bbe8ecf9da1aebacca99061663f498db3976c71b97794155136a828264",
      "entryHash": "71c4518ab59a4b4f1016046a72f368bb128c27a0e9d59a1edc1dd923c7b06391"
    },
    {
      "seq": 711,
      "prevHash": "71c4518ab59a4b4f1016046a72f368bb128c27a0e9d59a1edc1dd923c7b06391",
      "recordHash": "f3cc5fc61be9599a701ffb2056c6264b7b3042c41410f984534e2f41dea4374b",
      "entryHash": "5081de15db2b7da323181c02f37d689f93989c3c0b8a7cc4127a608638788868"
    },
    {
      "seq": 712,
      "prevHash": "5081de15db2b7da323181c02f37d689f93989c3c0b8a7cc4127a608638788868",
      "recordHash": "5e03e5e0f302db1e8f1bead82c2b6bdb9665dd1633316370b9397d69766a32d2",
      "entryHash": "e14e8445b5b824f2b8a280400d175eb04443ecf741f6071819ebb0841348ff1f"
    },
    {
      "seq": 713,
      "prevHash": "e14e8445b5b824f2b8a280400d175eb04443ecf741f6071819ebb0841348ff1f",
      "recordHash": "b1b2b6ca37fe872e441f53b32788da075080ee604e97366400f7cf748b1c2c9d",
      "entryHash": "30c1af60761ab8b60818c2d9667a454c4e252128c1eb94f03799fcd9cf1cfd3e"
    },
    {
      "seq": 714,
      "prevHash": "30c1af60761ab8b60818c2d9667a454c4e252128c1eb94f03799fcd9cf1cfd3e",
      "recordHash": "07b27438a4ba31f0a07ba81c963f403e572328c6a30c5242a074064f91d21b8f",
      "entryHash": "73ce115e5e787e5565f72eb71fe73fa4e858da9ce60b48c02540f2c135f3bbac"
    },
    {
      "seq": 715,
      "prevHash": "73ce115e5e787e5565f72eb71fe73fa4e858da9ce60b48c02540f2c135f3bbac",
      "recordHash": "7d904b7b542a6f2f265a2bc85b94b759900918dec27db190a26c30551585f9b1",
      "entryHash": "8e8f5fa320828d9afade0e476d747a589f344bd254e37b98c70c6aa18053ff72"
    },
    {
      "seq": 716,
      "prevHash": "8e8f5fa320828d9afade0e476d747a589f344bd254e37b98c70c6aa18053ff72",
      "recordHash": "5669689c19b0ef65ebb5fe79ee48c6f74b73aef2a7854650c7472439ce087a1d",
      "entryHash": "0fb405a549244cda1e3ada599abdeb409bb08de18c828d83d48733520fd8783c"
    },
    {
      "seq": 717,
      "prevHash": "0fb405a549244cda1e3ada599abdeb409bb08de18c828d83d48733520fd8783c",
      "recordHash": "2dd2db37c70eef81cab731b8bec9ee5686c9067e4718292469c63b5fe485a5cb",
      "entryHash": "fbb9e9a33c6c63117e7f9e075eaca9ee8c03bb85f051fea134f2e9d80aa86904"
    },
    {
      "seq": 718,
      "prevHash": "fbb9e9a33c6c63117e7f9e075eaca9ee8c03bb85f051fea134f2e9d80aa86904",
      "recordHash": "47893e458566846fb131d920a27cb8ca186f4105abce0fcef3c1823c1548e8af",
      "entryHash": "5d3df3db74395b3d5834509e3bff66ba5b743e8b9b087b72d7f06a897257c2cf"
    },
    {
      "seq": 719,
      "prevHash": "5d3df3db74395b3d5834509e3bff66ba5b743e8b9b087b72d7f06a897257c2cf",
      "recordHash": "b61c7b837881a867dfb56fd9bac94b2e126fcdd86989f2f577659dfff1b472ca",
      "entryHash": "3190643cd35bd005a05e63c512337f44c40439644c7a343db050633564b70ebb"
    },
    {
      "seq": 720,
      "prevHash": "3190643cd35bd005a05e63c512337f44c40439644c7a343db050633564b70ebb",
      "recordHash": "c646efb4134edfcef03cbc2c8e5e450798b266db5df6b01f62cffe1c608a62d7",
      "entryHash": "f32c501cae83c50c7748cc289a2b06f9d59c0d49c16de7f7919ded1cec170efd"
    },
    {
      "seq": 721,
      "prevHash": "f32c501cae83c50c7748cc289a2b06f9d59c0d49c16de7f7919ded1cec170efd",
      "recordHash": "cfa0d0db1347fdcecc69d2b5a985b8c7dec073cf203d9676a98c702bdfff7d18",
      "entryHash": "24d34ffc3bc9a8ff9f6435fcc7608f3807a9e350e033bd521a2ec737bc862e87"
    },
    {
      "seq": 722,
      "prevHash": "24d34ffc3bc9a8ff9f6435fcc7608f3807a9e350e033bd521a2ec737bc862e87",
      "recordHash": "6ac81ca7b0e78e6a68ceac967237914414cd840e5818caacb44e4b18b6985466",
      "entryHash": "c8e34c648dc47d97d1b1dfc5735754eff318cdb63a26590a3c823cd82a256be6"
    },
    {
      "seq": 723,
      "prevHash": "c8e34c648dc47d97d1b1dfc5735754eff318cdb63a26590a3c823cd82a256be6",
      "recordHash": "64ca6965d0535a5003d8d58f52c00977b8ba2ef5072650dacd29b987605aa25c",
      "entryHash": "8438a485bba44dd58b0782ad96de86fd6db9ae0900c4be80089106729f3f9464"
    },
    {
      "seq": 724,
      "prevHash": "8438a485bba44dd58b0782ad96de86fd6db9ae0900c4be80089106729f3f9464",
      "recordHash": "05364c1883717941066766ded15b25c41d3d21a6da306320b8c3fdf3ed2078c5",
      "entryHash": "5788db738a17ccacf17e8fe4068279b0ea60be91ee2306e0fab74c4f60cfb548"
    },
    {
      "seq": 725,
      "prevHash": "5788db738a17ccacf17e8fe4068279b0ea60be91ee2306e0fab74c4f60cfb548",
      "recordHash": "fec590b7a2de0131d12797b1b849ed8dd7af883c71d4c5b95c9cca7e5b88dcf2",
      "entryHash": "3a2c985b011448b71fe54ecf21a7de8e952eb9dc0d533406132bf3a6a6dcdd66"
    },
    {
      "seq": 726,
      "prevHash": "3a2c985b011448b71fe54ecf21a7de8e952eb9dc0d533406132bf3a6a6dcdd66",
      "recordHash": "007d92acd79f323fb376fa9608a5a46b871fb4b0f8eda3ee34662ca6a0404142",
      "entryHash": "fb8f50ff2bbe8be89b0a5d22acd2afa5c2c8152992db4bfca5c9047b4db2352b"
    },
    {
      "seq": 727,
      "prevHash": "fb8f50ff2bbe8be89b0a5d22acd2afa5c2c8152992db4bfca5c9047b4db2352b",
      "recordHash": "f88fe00d6f2336df2d791dbac4d3811cf883c638d0e97a2fe0eab37fe274e450",
      "entryHash": "dfc450ffdb528d48e198f003d1ae152be62fdb2db2d6193e8a738a150915d556"
    },
    {
      "seq": 728,
      "prevHash": "dfc450ffdb528d48e198f003d1ae152be62fdb2db2d6193e8a738a150915d556",
      "recordHash": "96fa636fe782f140b6264073a3d04228b5419ebe5cd58003cc2cc2eee6b001a3",
      "entryHash": "57a5b7c8a02bfe7b93d3dc420092309a19f9861f3e048e471aeae6beab97b73f"
    },
    {
      "seq": 729,
      "prevHash": "57a5b7c8a02bfe7b93d3dc420092309a19f9861f3e048e471aeae6beab97b73f",
      "recordHash": "677b0c907306efda816826d604fa8ee94a03caab0996954bb92b8340b64053f9",
      "entryHash": "a9235e34429655ed7c390f309df18e7e1dbcf4ab52587c58be46167e17e27cd5"
    },
    {
      "seq": 730,
      "prevHash": "a9235e34429655ed7c390f309df18e7e1dbcf4ab52587c58be46167e17e27cd5",
      "recordHash": "fed3ea39d137f8b97bc0bdd07bc4338a6fdbc5f244979aa9c0650f0f4a192bdd",
      "entryHash": "a3bb49d02f7223fecb41ef25a27e29c2b5063a97dd1499270b14e6fd9b6d2b73"
    }
  ],
  "checkpoint": {
    "seq": 730,
    "kind": "checkpoint",
    "record": "{\"day\":\"2026-11-06\",\"entries\":729,\"head\":\"a9235e34429655ed7c390f309df18e7e1dbcf4ab52587c58be46167e17e27cd5\",\"site\":\"https://upfork.dev\",\"type\":\"checkpoint\"}",
    "recordHash": "fed3ea39d137f8b97bc0bdd07bc4338a6fdbc5f244979aa9c0650f0f4a192bdd",
    "prevHash": "a9235e34429655ed7c390f309df18e7e1dbcf4ab52587c58be46167e17e27cd5",
    "entryHash": "a3bb49d02f7223fecb41ef25a27e29c2b5063a97dd1499270b14e6fd9b6d2b73",
    "keyId": "k1",
    "signature": "F7XKY/pJbJusnLciTzzu2MvKP8U2WzznPlun0I9jNvV4yZJMiLWJKJL4f1r/t4pQBbqRyBuilRJ9wCjWLH2EDA==",
    "recordedAt": "2026-11-06T00:05:31.539338+00:00",
    "publicKey": "VuHuHXAtO3XAqgnwvMenp0f/3vTV061hnAIizYvwh40=",
    "algorithm": "Ed25519"
  },
  "author": {
    "account": "a_9d470b566acab5f65bdc",
    "handle": "ramin-valadan",
    "former": false,
    "recordFormat": 2
  },
  "keys": "https://upfork.dev/ledger/keys.json"
}