diff --git a/system/public/aesthetic.computer/assets/artnom/manifest.json b/system/public/aesthetic.computer/assets/artnom/manifest.json new file mode 100644 index 0000000000..5de114bc79 --- /dev/null +++ b/system/public/aesthetic.computer/assets/artnom/manifest.json @@ -0,0 +1,1246 @@ +{ + "schema": "https://aesthetic.computer/schemas/artnom-manifest-v1.json", + "generatedAt": "2026-07-21T19:24:52.465Z", + "license": "Museum metadata is CC0; image rights are recorded per object and images remain remote.", + "sources": [ + { + "id": "artic", + "label": "Art Institute of Chicago", + "api": "https://api.artic.edu/api/v1", + "url": "https://www.artic.edu/open-access/public-api", + "imageService": "https://www.artic.edu/iiif/2" + }, + { + "id": "getty-aat", + "label": "Getty Art & Architecture Thesaurus", + "api": "https://vocab.getty.edu/sparql", + "url": "https://www.getty.edu/research/tools/vocabularies/aat/" + } + ], + "categories": [ + { + "id": "aat:300021495", + "slug": "cubism", + "label": "Cubism", + "aatId": "300021495", + "aat": "http://vocab.getty.edu/aat/300021495", + "speech": "cubism", + "sourceTerms": [ + { + "source": "artic", + "id": "TM-7539", + "label": "Cubism" + } + ], + "artworks": [ + { + "id": "artic:111060", + "source": "artic", + "sourceId": "111060", + "title": "Daniel-Henry Kahnweiler", + "artist": "Pablo Picasso", + "date": "autumn 1910", + "classification": "oil on canvas", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539", + "artic:TM-14351" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/aebda29e-16b8-4393-6edc-805cdb6ba459/full/200,/0/default.jpg", + "alt": "A abstract painting in which numerous planes of brown and gray, along with highlights of black and white, compose the figure of a man. The most recognizable features are a pair of clasped hands and the wave of his dark hair.", + "api": "https://api.artic.edu/api/v1/artworks/111060", + "url": "https://www.artic.edu/artworks/111060" + }, + { + "id": "artic:8624", + "source": "artic", + "sourceId": "8624", + "title": "Portrait of Pablo Picasso", + "artist": "Juan Gris", + "date": "January–February 1912", + "classification": "oil on canvas", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/4f7ccf9f-a8ce-2bd8-2d15-a38b2e6caa7a/full/200,/0/default.jpg", + "alt": "Blue and brown Cubist painting of Picasso with painterdz palette.", + "api": "https://api.artic.edu/api/v1/artworks/8624", + "url": "https://www.artic.edu/artworks/8624" + }, + { + "id": "artic:5357", + "source": "artic", + "sourceId": "5357", + "title": "The Red Armchair", + "artist": "Pablo Picasso", + "date": "December 16, 1931", + "classification": "painting", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/c617e2f0-d5fe-0772-390e-6d8c83895815/full/200,/0/default.jpg", + "alt": "Cubist painting of woman, bright yellow, green, purple colors.", + "api": "https://api.artic.edu/api/v1/artworks/5357", + "url": "https://www.artic.edu/artworks/5357" + }, + { + "id": "artic:135430", + "source": "artic", + "sourceId": "135430", + "title": "Weeping Woman I", + "artist": "Pablo Picasso", + "date": "July 1, 1937", + "classification": "etching", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/9794c354-eccb-5b8c-628b-a664416c78e6/full/200,/0/default.jpg", + "alt": "Print of woman with distorted and exaggerated features holding handkerchief to face.", + "api": "https://api.artic.edu/api/v1/artworks/135430", + "url": "https://www.artic.edu/artworks/135430" + }, + { + "id": "artic:14435", + "source": "artic", + "sourceId": "14435", + "title": "Portrait of Picasso", + "artist": "Juan Gris", + "date": "1947", + "classification": "etching", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/fb424aff-7bcc-0191-c2c1-dd5be947cc6b/full/200,/0/default.jpg", + "alt": "Varyingly shaded geometric shapes compose the fractured image of a seated figure in a double-breasted jacket.", + "api": "https://api.artic.edu/api/v1/artworks/14435", + "url": "https://www.artic.edu/artworks/14435" + }, + { + "id": "artic:12874", + "source": "artic", + "sourceId": "12874", + "title": "Abstraction (Guitar and Glass)", + "artist": "Juan Gris", + "date": "1913", + "classification": "painting", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/9f91f7e3-daa8-8969-31c1-892d5af19404/full/200,/0/default.jpg", + "alt": "Geometric forms fragment a vertical composition containing a woodgrain table with newspapers, a guitar, and a stemmed wine glass. These objects are vividly painted within a V-shaped fragment at center. Muted, shadowlike versions of the objects appear outside it. The outline of the guitar is repeated in the V in brown, green, blue, and black alongside a fragment of brown floral wallpaper.", + "api": "https://api.artic.edu/api/v1/artworks/12874", + "url": "https://www.artic.edu/artworks/12874" + }, + { + "id": "artic:44084", + "source": "artic", + "sourceId": "44084", + "title": "Woman with a Bird Cage", + "artist": "Rufino Tamayo", + "date": "1941", + "classification": "painting", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/a55d85fc-616b-629d-907b-75a35d4bb5c7/full/200,/0/default.jpg", + "alt": "A work made of oil on canvas.", + "api": "https://api.artic.edu/api/v1/artworks/44084", + "url": "https://www.artic.edu/artworks/44084" + }, + { + "id": "artic:180666", + "source": "artic", + "sourceId": "180666", + "title": "Bal (recto); Guitar (verso)", + "artist": "Georges Braque", + "date": "1912", + "classification": "drawings (visual works)", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/e883470a-422e-20e6-0dc1-dbab6e500ead/full/200,/0/default.jpg", + "alt": "A work made of collage composed of charcoal and cut-and-pasted commercially printed tan wove papers (recto) and charcoal (verso) on white laid paper.", + "api": "https://api.artic.edu/api/v1/artworks/180666", + "url": "https://www.artic.edu/artworks/180666" + }, + { + "id": "artic:265982", + "source": "artic", + "sourceId": "265982", + "title": "Still Life with a Box of Matches (Nature morte à la boite d'allumettes)", + "artist": "María Blanchard", + "date": "1918", + "classification": "painting", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539", + "artic:TM-7541" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/b4d6fb4f-f982-4ce7-ede8-4358655fe2e6/full/200,/0/default.jpg", + "alt": "Abstract, Cubist painting of objects on a table, highly geometric and boldy rendered in angular shapes of yellow, green blue, and brown. Errant details and textures emerge, often indecipherable as specific objects, but a small book of matches at center is clearly rendered, down to the writing on its cover.", + "api": "https://api.artic.edu/api/v1/artworks/265982", + "url": "https://www.artic.edu/artworks/265982" + }, + { + "id": "artic:244527", + "source": "artic", + "sourceId": "244527", + "title": "Du cubisme", + "artist": "Albert Gleizes", + "date": "Paris: Compagnie Française des Arts Graphiques, 1947", + "classification": "etching", + "style": "Cubism", + "sourceTerms": [ + "artic:TM-7539", + "artic:TM-13130" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021495" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/b60132af-f4f8-bf05-727f-889497f8af25/full/200,/0/default.jpg", + "alt": "A work made of unbound book in a cardboard slip case; intaglio prints by various artists.", + "api": "https://api.artic.edu/api/v1/artworks/244527", + "url": "https://www.artic.edu/artworks/244527" + } + ] + }, + { + "id": "aat:300021503", + "slug": "impressionism", + "label": "Impressionism", + "aatId": "300021503", + "aat": "http://vocab.getty.edu/aat/300021503", + "speech": "impressionism", + "sourceTerms": [ + { + "source": "artic", + "id": "TM-7543", + "label": "Impressionism" + } + ], + "artworks": [ + { + "id": "artic:20684", + "source": "artic", + "sourceId": "20684", + "title": "Paris Street; Rainy Day", + "artist": "Gustave Caillebotte", + "date": "1877", + "classification": "oil on canvas", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/f8fd76e9-c396-5678-36ed-6a348c904d27/full/200,/0/default.jpg", + "alt": "Life-size painting of an urban scene in Paris. A man in a top hat holding an umbrella and a woman in a long fashionable dark dress walk arm in arm toward the viewer as other city dwellers with umbrellas walk in various directions across cobblestone roads and sidewalks.", + "api": "https://api.artic.edu/api/v1/artworks/20684", + "url": "https://www.artic.edu/artworks/20684" + }, + { + "id": "artic:64818", + "source": "artic", + "sourceId": "64818", + "title": "Stacks of Wheat (End of Summer)", + "artist": "Claude Monet", + "date": "1890–91", + "classification": "oil on canvas", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/a38e2828-ec6f-ece1-a30f-70243449197b/full/200,/0/default.jpg", + "alt": "Painting composed of short, dense brushstrokes depicts two domed stacks of wheat that cast long shadows on a field. The angled light indicates either a rising or setting sun.", + "api": "https://api.artic.edu/api/v1/artworks/64818", + "url": "https://www.artic.edu/artworks/64818" + }, + { + "id": "artic:16568", + "source": "artic", + "sourceId": "16568", + "title": "Water Lilies", + "artist": "Claude Monet", + "date": "1906", + "classification": "oil on canvas", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-13130" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/3c27b499-af56-f0d5-93b5-a7f2f1ad5813/full/200,/0/default.jpg", + "alt": "Painting of a pond seen up close spotted with thickly painted pink and white water lilies and a shadow across the top third of the picture.", + "api": "https://api.artic.edu/api/v1/artworks/16568", + "url": "https://www.artic.edu/artworks/16568" + }, + { + "id": "artic:14655", + "source": "artic", + "sourceId": "14655", + "title": "Two Sisters (On the Terrace)", + "artist": "Pierre-Auguste Renoir", + "date": "1881", + "classification": "painting", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/3a608f55-d76e-fa96-d0b1-0789fbc48f1e/full/200,/0/default.jpg", + "alt": "Impressionist painting of two girls on balcony, bright flower hat, knitting basket.", + "api": "https://api.artic.edu/api/v1/artworks/14655", + "url": "https://www.artic.edu/artworks/14655" + }, + { + "id": "artic:111442", + "source": "artic", + "sourceId": "111442", + "title": "The Child's Bath", + "artist": "Mary Cassatt", + "date": "1893", + "classification": "painting", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/3b885ae0-4d46-5fe4-d70a-00474827f02c/full/200,/0/default.jpg", + "alt": "Painting of mother in blue, purple, and green-stripped dress washing child's feet.", + "api": "https://api.artic.edu/api/v1/artworks/111442", + "url": "https://www.artic.edu/artworks/111442" + }, + { + "id": "artic:16571", + "source": "artic", + "sourceId": "16571", + "title": "Arrival of the Normandy Train, Gare Saint-Lazare", + "artist": "Claude Monet", + "date": "1877", + "classification": "painting", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/0f1cc0e0-e42e-be16-3f71-2022da38cb93/full/200,/0/default.jpg", + "alt": "Loosely painted image of an open-air train station. On the right, a parked train gives off an enormous plumb of white smoke, making the scene look as though it were full of clouds. A huddled mass of barely discernible people crowd around the train on both sides of the tracks. Blue, green, and gray tones dominate.", + "api": "https://api.artic.edu/api/v1/artworks/16571", + "url": "https://www.artic.edu/artworks/16571" + }, + { + "id": "artic:11723", + "source": "artic", + "sourceId": "11723", + "title": "Woman at Her Toilette", + "artist": "Berthe Morisot", + "date": "1875–80", + "classification": "painting", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/78c80988-6524-cec7-c661-a4c0a706d06f/full/200,/0/default.jpg", + "alt": "Painted portrait dominated by loose, large brushstrokes of a woman, seen from the back, wearing a gauzy white off-the-shoulder dress and black choker, blond hair swept up, examining herself in a mirror at left. The background is a feathered swirl suggesting floral pattern in cool pale tones of gray, purple, pink, and blue.", + "api": "https://api.artic.edu/api/v1/artworks/11723", + "url": "https://www.artic.edu/artworks/11723" + }, + { + "id": "artic:25865", + "source": "artic", + "sourceId": "25865", + "title": "The Herring Net", + "artist": "Winslow Homer", + "date": "1885", + "classification": "painting", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-7548", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/5dca7347-c6dc-24dd-d073-d705b9cdc575/full/200,/0/default.jpg", + "alt": "Painting of two fishermen in small wooden boat on rocky seas.", + "api": "https://api.artic.edu/api/v1/artworks/25865", + "url": "https://www.artic.edu/artworks/25865" + }, + { + "id": "artic:61603", + "source": "artic", + "sourceId": "61603", + "title": "Ballet at the Paris Opéra", + "artist": "Hilaire Germain Edgar Degas", + "date": "1877", + "classification": "pastel", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/cb34b0a8-bc51-d063-aab1-47c7debf3a7b/full/200,/0/default.jpg", + "alt": "Color pastel drawing of ballerinas in tutus on stage, watched by audience.", + "api": "https://api.artic.edu/api/v1/artworks/61603", + "url": "https://www.artic.edu/artworks/61603" + }, + { + "id": "artic:69780", + "source": "artic", + "sourceId": "69780", + "title": "The Fountain, Villa Torlonia, Frascati, Italy", + "artist": "John Singer Sargent", + "date": "1907", + "classification": "oil on canvas", + "style": "Impressionism", + "sourceTerms": [ + "artic:TM-7543", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021503" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/3f9aa9db-61e1-7060-fdb0-bfd7e41ddd08/full/200,/0/default.jpg", + "alt": "Portrait of a woman in white with a bonnet perched on an architectural feature painting something to her right, a spouting water fountain, greenery, and a an ornate, low white fence behind her. Beside her, a man leans back, eying the canvas.", + "api": "https://api.artic.edu/api/v1/artworks/69780", + "url": "https://www.artic.edu/artworks/69780" + } + ] + }, + { + "id": "aat:300021508", + "slug": "post-impressionism", + "label": "Post-Impressionism", + "aatId": "300021508", + "aat": "http://vocab.getty.edu/aat/300021508", + "speech": "post-impressionism", + "sourceTerms": [ + { + "source": "artic", + "id": "TM-7547", + "label": "Post-Impressionism" + } + ], + "artworks": [ + { + "id": "artic:28560", + "source": "artic", + "sourceId": "28560", + "title": "The Bedroom", + "artist": "Vincent van Gogh", + "date": "1889", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-13203", + "artic:TM-15636" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/6644829f-f292-c5c4-a73c-0356a6fdbf0d/full/200,/0/default.jpg", + "alt": "Painting of bedroom, blue walls, green window, tan bed, red bedding.", + "api": "https://api.artic.edu/api/v1/artworks/28560", + "url": "https://www.artic.edu/artworks/28560" + }, + { + "id": "artic:80607", + "source": "artic", + "sourceId": "80607", + "title": "Self-Portrait", + "artist": "Vincent van Gogh", + "date": "1887", + "classification": "oil on board", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-13203", + "artic:TM-15636" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/47c5bcb8-62ef-e5d7-55e7-f5121f409a30/full/200,/0/default.jpg", + "alt": "Painting of a red-haired, bearded man with light skin, painted in short brushstrokes and multicolored dots. The background is likewise a mass of small, closely spaced colored dots, these in green, blue, and red-orange.", + "api": "https://api.artic.edu/api/v1/artworks/80607", + "url": "https://www.artic.edu/artworks/80607" + }, + { + "id": "artic:61128", + "source": "artic", + "sourceId": "61128", + "title": "At the Moulin Rouge", + "artist": "Henri de Toulouse-Lautrec", + "date": "1892–95", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/defb4004-b500-218d-3d9b-9a02423f097d/full/200,/0/default.jpg", + "alt": "Painting, heavy in tones of blue-green and burnt orange, depicting a bar scene at night. The men wear top hats and many are bearded; the women are dressed in voluminous gowns and decorative hats. Five people converse in the center of the composition and a woman with a green face dominates the right foreground.", + "api": "https://api.artic.edu/api/v1/artworks/61128", + "url": "https://www.artic.edu/artworks/61128" + }, + { + "id": "artic:111436", + "source": "artic", + "sourceId": "111436", + "title": "The Basket of Apples", + "artist": "Paul Cezanne", + "date": "c. 1893", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/52ac8996-3460-cf71-cb42-5c4d0aa29b74/full/200,/0/default.jpg", + "alt": "Painting of a basket of green and red-yellow apples spilling over into a white cloth placed on a wooden table, with a dark green bottle and a plate of pale biscuits behind.", + "api": "https://api.artic.edu/api/v1/artworks/111436", + "url": "https://www.artic.edu/artworks/111436" + }, + { + "id": "artic:154235", + "source": "artic", + "sourceId": "154235", + "title": "The Girl by the Window", + "artist": "Edvard Munch", + "date": "1893", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/d7df2633-3b40-f570-c906-211503a37cde/full/200,/0/default.jpg", + "alt": "Loosely painted scene of a brown-haired girl in her nightgown standing in a dark room, gazing out a window into a brighter night, her face unseen and her hand drawing back the pale curtain. Light blues and pink whites dominate the girl and the window, while deep browns, blues, and greens color the darkened portions of her room.", + "api": "https://api.artic.edu/api/v1/artworks/154235", + "url": "https://www.artic.edu/artworks/154235" + }, + { + "id": "artic:16487", + "source": "artic", + "sourceId": "16487", + "title": "The Bay of Marseille, Seen from L'Estaque", + "artist": "Paul Cezanne", + "date": "c. 1885", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/d4ca6321-8656-3d3f-a362-2ee297b2b813/full/200,/0/default.jpg", + "alt": "Impressionist sea landscape, tan houses, blue ocean, distant mountains.", + "api": "https://api.artic.edu/api/v1/artworks/16487", + "url": "https://www.artic.edu/artworks/16487" + }, + { + "id": "artic:144361", + "source": "artic", + "sourceId": "144361", + "title": "Earthly Paradise", + "artist": "Pierre Bonnard", + "date": "1916–20", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/8b4abeed-c22b-1477-1e76-b303ac1415d1/full/200,/0/default.jpg", + "alt": "Vibrantly colored painting of nude woman lying, and man standing, in lush landscape.", + "api": "https://api.artic.edu/api/v1/artworks/144361", + "url": "https://www.artic.edu/artworks/144361" + }, + { + "id": "artic:16146", + "source": "artic", + "sourceId": "16146", + "title": "Equestrienne (At the Cirque Fernando)", + "artist": "Henri de Toulouse-Lautrec", + "date": "1887–88", + "classification": "oil on canvas", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-15636", + "artic:TM-13203" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/65db9e21-83c3-1cc6-7240-1e1996d87f52/full/200,/0/default.jpg", + "alt": "Painting of a performance inside a circus ring. A man wearing a suit holds a whip in his hand and looks towards a woman with red hair riding a gray horse and wearing a green dress. Audience members wearing suits and top hats watch from red and white benches on the side.", + "api": "https://api.artic.edu/api/v1/artworks/16146", + "url": "https://www.artic.edu/artworks/16146" + }, + { + "id": "artic:19339", + "source": "artic", + "sourceId": "19339", + "title": "Arlésiennes (Mistral)", + "artist": "Paul Gauguin", + "date": "1888", + "classification": "painting", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547", + "artic:TM-13203", + "artic:TM-15636" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/ae6ac285-6887-dd76-4570-d379c786dfae/full/200,/0/default.jpg", + "alt": "Painting of four women with blue shawls walking near red fence.", + "api": "https://api.artic.edu/api/v1/artworks/19339", + "url": "https://www.artic.edu/artworks/19339" + }, + { + "id": "artic:64724", + "source": "artic", + "sourceId": "64724", + "title": "The Home of the Heron", + "artist": "George Inness", + "date": "1893", + "classification": "painting", + "style": "Post-Impressionism", + "sourceTerms": [ + "artic:TM-7547" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021508" + ], + "publicDomain": true, + "rights": "https://creativecommons.org/publicdomain/mark/1.0/", + "image": "https://www.artic.edu/iiif/2/0f2d999d-0173-2935-a6d0-0175bb97b2a9/full/200,/0/default.jpg", + "alt": "A herondz silhouette sits amongst the brush in a golden hued forest.", + "api": "https://api.artic.edu/api/v1/artworks/64724", + "url": "https://www.artic.edu/artworks/64724" + } + ] + }, + { + "id": "aat:300021512", + "slug": "surrealism", + "label": "Surrealism", + "aatId": "300021512", + "aat": "http://vocab.getty.edu/aat/300021512", + "speech": "surrealism", + "sourceTerms": [ + { + "source": "artic", + "id": "TM-7551", + "label": "Surrealism" + } + ], + "artworks": [ + { + "id": "artic:34181", + "source": "artic", + "sourceId": "34181", + "title": "La durée poignardée (Time Transfixed)", + "artist": "René Magritte", + "date": "1938", + "classification": "oil on canvas", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/91e4c46b-9ef3-ca78-148c-86845e457d88/full/200,/0/default.jpg", + "alt": "A rather stark, clean-lined painting shows a fireplace mantle topped with a clock against a bare wall with wood paneling. From the back wall of the fireplace, a black locomotive emerges, belching smoke and riding an unseen track in the the air.", + "api": "https://api.artic.edu/api/v1/artworks/34181", + "url": "https://www.artic.edu/artworks/34181" + }, + { + "id": "artic:151424", + "source": "artic", + "sourceId": "151424", + "title": "Inventions of the Monsters", + "artist": "Salvador Dalí", + "date": "1937", + "classification": "painting", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/be9551d4-860f-37a0-1408-086617f1824e/full/200,/0/default.jpg", + "alt": "A painting of strange and surreal scene in a desert landscape with mountains in the background. The scene features a giraffe that is on fire, disembodied forms and faces, and skeletal figures.", + "api": "https://api.artic.edu/api/v1/artworks/151424", + "url": "https://www.artic.edu/artworks/151424" + }, + { + "id": "artic:56682", + "source": "artic", + "sourceId": "56682", + "title": "The Rock", + "artist": "Peter Blume", + "date": "1944–48", + "classification": "painting", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/2843f22e-8ef1-2cd5-b53e-6712cd664e9a/full/200,/0/default.jpg", + "alt": "Painting of figures construction project amid smoky destruction, middle is pink crater.", + "api": "https://api.artic.edu/api/v1/artworks/56682", + "url": "https://www.artic.edu/artworks/56682" + }, + { + "id": "artic:83905", + "source": "artic", + "sourceId": "83905", + "title": "That Which I Should Have Done I Did Not Do (The Door)", + "artist": "Ivan Albright", + "date": "1931–41", + "classification": "oil on canvas", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/0ecc7b1a-70b3-7ac6-1a46-3401d0963aa6/full/200,/0/default.jpg", + "alt": "Shallow and tall painting tightly focused on a black paneled door, a wreath of dying flowers hanging on it and a hand reaching to the doorknob from left.", + "api": "https://api.artic.edu/api/v1/artworks/83905", + "url": "https://www.artic.edu/artworks/83905" + }, + { + "id": "artic:185184", + "source": "artic", + "sourceId": "185184", + "title": "Venus de Milo with Drawers", + "artist": "Salvador Dalí", + "date": "1936", + "classification": "sculpture", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/678c7a7a-b8c7-2857-7b97-f3c3501b8b1c/full/200,/0/default.jpg", + "alt": "White plaster statue of woman with protruding compartments, each adorned with fur pompom.", + "api": "https://api.artic.edu/api/v1/artworks/185184", + "url": "https://www.artic.edu/artworks/185184" + }, + { + "id": "artic:189807", + "source": "artic", + "sourceId": "189807", + "title": "Object", + "artist": "Claude Cahun", + "date": "1936", + "classification": "wood", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/07c85e2a-d045-1e2e-ab12-4887e82a1f7f/full/200,/0/default.jpg", + "alt": "Three-dimensional piece featuring a large eyeball, oriented sideways, on a wooden pin set in a flat piece of yellow wood at upper right, cursive writing in French on the wood. Strands of curly hair and the wooden cutout of a cloud are affixed to the eyeball. Affixed to the wood, at lower left, is a tiny light-skinned hand with fingers reaching upward.", + "api": "https://api.artic.edu/api/v1/artworks/189807", + "url": "https://www.artic.edu/artworks/189807" + }, + { + "id": "artic:118661", + "source": "artic", + "sourceId": "118661", + "title": "Juan Soriano De Lacandón", + "artist": "Leonora Carrington", + "date": "1964", + "classification": "painting", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-12062" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/b59b290e-1ea6-dc37-4c86-1a16c948bfed/full/200,/0/default.jpg", + "alt": "A work made of graphite and casein on panel.", + "api": "https://api.artic.edu/api/v1/artworks/118661", + "url": "https://www.artic.edu/artworks/118661" + }, + { + "id": "artic:72728", + "source": "artic", + "sourceId": "72728", + "title": "Untitled", + "artist": "Yves Tanguy", + "date": "1928", + "classification": "oil on panel", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/64b620f1-4467-7e17-4356-d7537850aa5f/full/200,/0/default.jpg", + "alt": "Large painting on seven panels of dark landscape with small abstract shapes.", + "api": "https://api.artic.edu/api/v1/artworks/72728", + "url": "https://www.artic.edu/artworks/72728" + }, + { + "id": "artic:198809", + "source": "artic", + "sourceId": "198809", + "title": "Self-Portrait of My Sister", + "artist": "Gertrude Abercrombie", + "date": "1941", + "classification": "oil on canvas", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/ae4ffb7f-58e4-7a2a-a960-0ec9a46a09ea/full/200,/0/default.jpg", + "alt": "A work made of oil on canvas.", + "api": "https://api.artic.edu/api/v1/artworks/198809", + "url": "https://www.artic.edu/artworks/198809" + }, + { + "id": "artic:93798", + "source": "artic", + "sourceId": "93798", + "title": "Picture of Dorian Gray", + "artist": "Ivan Albright", + "date": "1943–44", + "classification": "painting", + "style": "Surrealism", + "sourceTerms": [ + "artic:TM-7551", + "artic:TM-5981" + ], + "aat": [ + "http://vocab.getty.edu/aat/300021512" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/aa2b57e9-3773-03c1-0fb4-0135e7358dc6/full/200,/0/default.jpg", + "alt": "A graphic, grotesque painting of a balding, bug-eyed man with white hair and decaying flesh and open boils in dominant shades of gray and magenta, his clothing tattered and torn. Behind him, a crumbling home is suggested through objects and voids of space rendered in swirls of prismatic colors like those reflected by an oil slick: dark purples, greens, blues.", + "api": "https://api.artic.edu/api/v1/artworks/93798", + "url": "https://www.artic.edu/artworks/93798" + } + ] + }, + { + "id": "aat:300022205", + "slug": "pop-art", + "label": "Pop Art", + "aatId": "300022205", + "aat": "http://vocab.getty.edu/aat/300022205", + "speech": "pop art", + "sourceTerms": [ + { + "source": "artic", + "id": "TM-7221", + "label": "Pop Art" + } + ], + "artworks": [ + { + "id": "artic:47149", + "source": "artic", + "sourceId": "47149", + "title": "Mao", + "artist": "Andy Warhol", + "date": "1972", + "classification": "painting", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/97cb5c4b-cce5-c4d8-f303-613ce92832c7/full/200,/0/default.jpg", + "alt": "Large painted portrait of Mao wearing eyeshadow, blush, blue backdrop.", + "api": "https://api.artic.edu/api/v1/artworks/47149", + "url": "https://www.artic.edu/artworks/47149" + }, + { + "id": "artic:102234", + "source": "artic", + "sourceId": "102234", + "title": "American Collectors (Fred and Marcia Weisman)", + "artist": "David Hockney", + "date": "1968", + "classification": "painting", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/f95a0a70-90c5-2806-2a66-68f27f8c0735/full/200,/0/default.jpg", + "alt": "A painting of a light-skinned man in a suit and glasses standing in profile and a light-skinned woman with chin-length gray hair and wearing a full-length bright-pink robe stand in a courtyard with various sculptures. Between them is a large stone on a plinth, behind them a green abstracted seated figure, and off to the right a totem pole.", + "api": "https://api.artic.edu/api/v1/artworks/102234", + "url": "https://www.artic.edu/artworks/102234" + }, + { + "id": "artic:122054", + "source": "artic", + "sourceId": "122054", + "title": "Alka Seltzer", + "artist": "Roy Lichtenstein", + "date": "1966", + "classification": "crayon", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/3968921f-222e-37ae-5c53-fafd26a45c7b/full/200,/0/default.jpg", + "alt": "Depiction of comic book style cup, alka seltzer dissolving in water.", + "api": "https://api.artic.edu/api/v1/artworks/122054", + "url": "https://www.artic.edu/artworks/122054" + }, + { + "id": "artic:24836", + "source": "artic", + "sourceId": "24836", + "title": "Brushstroke with Spatter", + "artist": "Roy Lichtenstein", + "date": "1966", + "classification": "painting", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/f8dbda50-a10b-d0ff-a35c-24c2f9e733f1/full/200,/0/default.jpg", + "alt": "Oil painting in cartoon style of gigantic, close-up brushstroke of paint, blue spatter.", + "api": "https://api.artic.edu/api/v1/artworks/24836", + "url": "https://www.artic.edu/artworks/24836" + }, + { + "id": "artic:229406", + "source": "artic", + "sourceId": "229406", + "title": "Liz #3 [Early Colored Liz]", + "artist": "Andy Warhol", + "date": "1963", + "classification": "acrylic paintings (visual works)", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/362c0c2a-4576-2207-4ba9-2b87e6d0bad0/full/200,/0/default.jpg", + "alt": "A work made of acrylic and silkscreen ink on linen.", + "api": "https://api.artic.edu/api/v1/artworks/229406", + "url": "https://www.artic.edu/artworks/229406" + }, + { + "id": "artic:184362", + "source": "artic", + "sourceId": "184362", + "title": "Mirror #3 (Six Panels)", + "artist": "Roy Lichtenstein", + "date": "1971", + "classification": "painting", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221", + "artic:TM-7536" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/4f3baaee-14ea-81e0-6252-447ab15a9b1d/full/200,/0/default.jpg", + "alt": "Abstract work of numerous vertical lines dividing a horizontal image, making rectangular shapes in black, white, yellow, and white with tiny black dots forming the effect of a gray gradient. Many of the shapes have diagonal white bands running irregularly across them, evoking reflective surfaces of different angles.", + "api": "https://api.artic.edu/api/v1/artworks/184362", + "url": "https://www.artic.edu/artworks/184362" + }, + { + "id": "artic:150044", + "source": "artic", + "sourceId": "150044", + "title": "Marilyn Monroe (Marilyn)", + "artist": "Andy Warhol", + "date": "1967", + "classification": "screenprint", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/89255d96-36c4-e941-8b13-a1b165a07a49/full/200,/0/default.jpg", + "alt": "A work made of color screenprint on cream card.", + "api": "https://api.artic.edu/api/v1/artworks/150044", + "url": "https://www.artic.edu/artworks/150044" + }, + { + "id": "artic:109915", + "source": "artic", + "sourceId": "109915", + "title": "Clothespin", + "artist": "Claes Oldenburg", + "date": "1975", + "classification": "sculpture", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/daae5ff6-e38a-19bc-78e6-d9134a527af5/full/200,/0/default.jpg", + "alt": "A work made of cor-ten and stainless steel.", + "api": "https://api.artic.edu/api/v1/artworks/109915", + "url": "https://www.artic.edu/artworks/109915" + }, + { + "id": "artic:146886", + "source": "artic", + "sourceId": "146886", + "title": "Untitled", + "artist": "Keith Haring", + "date": "September 27, 1981", + "classification": "ink or chalk wash", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/f8f58142-0be4-d504-b309-61ed72bc547c/full/200,/0/default.jpg", + "alt": "A work made of brush and black ink on white board.", + "api": "https://api.artic.edu/api/v1/artworks/146886", + "url": "https://www.artic.edu/artworks/146886" + }, + { + "id": "artic:100864", + "source": "artic", + "sourceId": "100864", + "title": "Sunday Morning, Mayflower Hotel, N.Y.", + "artist": "David Hockney", + "date": "November 28, 1983", + "classification": "color photograph", + "style": "Pop Art", + "sourceTerms": [ + "artic:TM-7221" + ], + "aat": [ + "http://vocab.getty.edu/aat/300022205" + ], + "publicDomain": false, + "rights": "http://rightsstatements.org/vocab/InC/1.0/", + "image": "https://www.artic.edu/iiif/2/c04a376f-23fc-65da-bc10-6af4b77f120a/full/200,/0/default.jpg", + "alt": "A work made of photocollage (chromogenic prints).", + "api": "https://api.artic.edu/api/v1/artworks/100864", + "url": "https://www.artic.edu/artworks/100864" + } + ] + } + ] +} diff --git a/system/public/aesthetic.computer/disks/artnom.mjs b/system/public/aesthetic.computer/disks/artnom.mjs new file mode 100644 index 0000000000..1e1c91f49e --- /dev/null +++ b/system/public/aesthetic.computer/disks/artnom.mjs @@ -0,0 +1,19 @@ +// Artnom, 2026.07.21 +// The art-history edition of nom: every answer is a museum thumbnail and every +// round is identified by a Getty AAT style URI. Artwork metadata and rights +// come from the generated assets/artnom/manifest.json catalog snapshot. + +import { boot as nomBoot, sim, paint, act, makeMeta } from "../lib/nom.mjs"; + +async function boot(api) { + const artnom = await api.net.preload( + "/aesthetic.computer/assets/artnom/manifest.json", + ); + nomBoot({ ...api, params: ["art"], artnom }); +} + +function meta() { + return makeMeta(["art"]); +} + +export { boot, sim, paint, act, meta }; diff --git a/system/public/aesthetic.computer/lib/nom.mjs b/system/public/aesthetic.computer/lib/nom.mjs index 728b85fc88..2a5520ce4f 100644 --- a/system/public/aesthetic.computer/lib/nom.mjs +++ b/system/public/aesthetic.computer/lib/nom.mjs @@ -1,8 +1,9 @@ -// nom — shared engine for the muncher games (numbnom / engnom / mexinom / dannom / rusnom / notenom / catnom), 2026.06.07 +// nom — shared engine for the muncher games (numbnom / engnom / mexinom / dannom / rusnom / notenom / catnom / artnom), 2026.06.07 // Move the muncher around a 5×5 grid and eat every square that satisfies the // rule at the top — but dodge the Troggles. `numbnom` = numbers, `engnom` = words, // `notenom` = musical notes (note mode voices each square + plays the scale), -// `catnom` = Categories-game rounds (AC pieces, code words, slang, vibes). +// `catnom` = Categories-game rounds (AC pieces, code words, slang, vibes), +// `artnom` = museum thumbnails grouped by Getty AAT art-historical styles. import { Synth } from "./synth.mjs"; // shared virtual synth + perc kit @@ -19,6 +20,7 @@ import { Synth } from "./synth.mjs"; // shared virtual synth + perc kit munchers:words → words munchers:notes → musical notes (notenom) munchers:cat → categories (catnom) + munchers:art → art history thumbnails (artnom) #endregion */ /* #region 🏁 TODO @@ -31,7 +33,7 @@ const COLS = 5; const ROWS = 5; // 🌐 Game state -let mode = "number"; // "number" | "word" | "note" +let mode = "number"; // "number" | "word" | "note" | "art" let lang = "en"; // word language: "en" engnom | "es" mexinom | "da" dannom | "ru" rusnom | "cat" catnom let hiRes = false; // 🖼️ render via the hd() native-resolution Canvas2D layer (opt-in per edition) let noteScale = []; // current board's scale (note mode), played on board start @@ -59,6 +61,11 @@ let mouth = 0; // muncher mouth animation phase let troggleClock = null; let message = ""; // transient feedback line let messageTimer = 0; +let artCatalog = null; // generated Getty-AAT/museum manifest (artnom only) +let artPreload = null; // net.preload captured at boot for remote IIIF thumbnails +let artLoadGeneration = 0; // ignores thumbnail promises from a prior boot +let artImages = new Map(); // remote image URL -> bitmap | "loading" | "failed" +let lastArtwork = null; // most recently munched object; I opens its museum page // 🔊 + ✨ Feel let snd = null; // live sound handle (captured each frame) @@ -134,17 +141,23 @@ function reset() { beatMs = BEAT_MS; nowMs = 0; beatIndex = 0; lastBeatIndex = null; beatsLeft = 0; beatsMax = 1; beatPhase = 0; beatPulse = 0; heldChord = null; introTimer = 0; pendingStart = false; + artCatalog = null; artPreload = null; artLoadGeneration += 1; + artImages.clear(); lastArtwork = null; } // 🥾 Boot — reset all state, then drop straight onto a live board (no title gate // / "press any key"); a brief intro card fades over the playing board. -function boot({ params, hud, clock, num: { randInt } }) { +function boot({ params, hud, clock, artnom, net, num: { randInt } }) { reset(); clock?.resync?.(); // network-sync the wall clock; board beats ride this grid const m = resolveMode(params); mode = m.mode; lang = m.lang; - hiRes = params?.includes?.("sd") !== true; // hi-res Canvas2D paint by default; "sd" opts out + artCatalog = artnom || null; + artPreload = net?.preload || null; + // The Canvas2D adapter intentionally has no bitmap-paste surface yet, so the + // image edition stays on the pixel renderer. All text editions remain HD. + hiRes = mode !== "art" && params?.includes?.("sd") !== true; hud?.label?.(""); // hide the top-left corner label resetGame({ randInt }); newRound({ randInt }); @@ -161,6 +174,7 @@ function boot({ params, hud, clock, num: { randInt } }) { // meta() would otherwise mislabel (e.g. dannom showing the number edition). function resolveMode(params) { const p = params?.[0]; + if (p === "art" || p === "artnom" || p === "museum") return { mode: "art", lang: "en" }; if (p === "spanish" || p === "es" || p === "mexi" || p === "mexinom") return { mode: "word", lang: "es" }; if (p === "danish" || p === "da" || p === "dansk" || p === "dannom") return { mode: "word", lang: "da" }; if (p === "russian" || p === "ru" || p === "russkiy" || p === "rusnom") return { mode: "word", lang: "ru" }; @@ -947,7 +961,33 @@ function newRound({ randInt }) { const nCorrect = min(total - 4, 5 + level + rnd(3)); const cells = []; - if (mode === "note") { + if (mode === "art") { + // 🖼️ One Getty AAT style, dealt from museum objects whose source taxonomy + // explicitly assigns that style. Decoys come from the other AAT rounds. + const categories = artCatalog?.categories || []; + if (!categories.length) throw new Error("Artnom catalog has no categories"); + const category = categories[rnd(categories.length)]; + ruleLabel = category.label.toUpperCase(); + ruleSpeech = category.speech || category.label; + noteScale = []; + const goods = shuffle([...(category.artworks || [])], rnd); + const bads = shuffle( + categories + .filter((candidate) => candidate.id !== category.id) + .flatMap((candidate) => candidate.artworks || []), + rnd, + ); + let nc = min(nCorrect, goods.length); + nc = max(nc, min(goods.length, total - bads.length)); + for (let i = 0; i < nc; i += 1) { + const artwork = goods[i]; + cells.push({ value: artwork.id, artwork, correct: true }); + } + for (let i = 0; i < total - nc; i += 1) { + const artwork = bads[i]; + cells.push({ value: artwork.id, artwork, correct: false }); + } + } else if (mode === "note") { // 🎵 Build a board of note squares satisfying (or breaking) a musical rule. const noteRound = NOTE_ROUNDS[rnd(NOTE_ROUNDS.length)]; const test = noteRound.test; @@ -1069,6 +1109,54 @@ function newRound({ randInt }) { chompPhase = 0; walkTarget = null; walkTick = 0; + lastArtwork = null; + if (mode === "art") preloadArtBoard(); +} + +// Hotlink the museum's documented 200px IIIF derivatives. The generated +// manifest keeps image URLs remote; this cache only lives for the current +// piece session and avoids re-decoding a work when it returns as a decoy. +function preloadArtBoard() { + if (!artPreload) return; + const generation = artLoadGeneration; + for (const cell of grid) { + const url = cell.artwork?.image; + if (!url || artImages.has(url)) continue; + artImages.set(url, "loading"); + artPreload(url) + .then((file) => { + if (generation !== artLoadGeneration) return; + artImages.set(url, squareArtThumbnail(file?.img) || "failed"); + }) + .catch(() => { + if (generation !== artLoadGeneration) return; + artImages.set(url, "failed"); + }); + } +} + +// Center-crop + downsample once when a museum image arrives. Rendering then +// scales a compact square buffer instead of recropping a large IIIF bitmap on +// every frame (25 tiles × 60fps would otherwise allocate continuously). +function squareArtThumbnail(source, size = 64) { + if (!source?.pixels || !source.width || !source.height) return null; + const side = min(source.width, source.height); + const ox = floor((source.width - side) / 2); + const oy = floor((source.height - side) / 2); + const pixels = new Uint8ClampedArray(size * size * 4); + for (let y = 0; y < size; y += 1) { + const sy = oy + min(side - 1, floor((y / size) * side)); + for (let x = 0; x < size; x += 1) { + const sx = ox + min(side - 1, floor((x / size) * side)); + const si = (sy * source.width + sx) * 4; + const di = (y * size + x) * 4; + pixels[di] = source.pixels[si]; + pixels[di + 1] = source.pixels[si + 1]; + pixels[di + 2] = source.pixels[si + 2]; + pixels[di + 3] = source.pixels[si + 3]; + } + } + return { width: size, height: size, pixels }; } function spawnTroggles(rnd) { @@ -1372,9 +1460,12 @@ function munch() { else chompGood(); camTargetZoom = 1.06; // tiny zoom punch on every good munch - // Collect the value + light up its (now frightened) duplicates. + if (mode === "art") revealArtwork(cell.artwork); + + // Collect the value + light up its (now frightened) duplicates. Artnom + // objects are unique visual answers, so its reveal lives in the caption. const v = String(cell.value); - if (!foundValues.includes(v)) foundValues.push(v); + if (mode !== "art" && !foundValues.includes(v)) foundValues.push(v); grid.forEach((c) => { if (!c.eaten && String(c.value) === v) { c.known = true; @@ -1386,7 +1477,7 @@ function munch() { // Speak the English translation (mexinom / dannom), else announce the last one. // Note mode stays musical — no speech, the note itself sounds. if (mode === "word" && lang !== "en") sayTranslation(v); - else if (mode !== "note" && remaining === 1) say("last one"); + else if (mode !== "note" && mode !== "art" && remaining === 1) say("last one"); if (remaining <= 0) { state = "clear"; clearPhase = 96; @@ -1401,13 +1492,31 @@ function munch() { cell.failed = true; // X it out — can't be tried again if (mode === "note") noteMunchBad(cell); else { - if (mode === "word" && lang !== "en") sayTranslation(cell.value); + if (mode === "art") revealArtwork(cell.artwork); + else if (mode === "word" && lang !== "en") sayTranslation(cell.value); chompBad(); } loseLife(); } } +// Museum-object feedback: speak the full credit, but keep the visual caption +// compact enough for a phone. The canonical record remains one I-key away. +function revealArtwork(artwork) { + if (!artwork) return; + lastArtwork = artwork; + const trim = (value, length) => { + const text = String(value || "unknown"); + return text.length > length ? `${text.slice(0, length - 1)}…` : text; + }; + caption = `${trim(artwork.title, 30)} — ${trim(artwork.artist, 22)}`; + captionFrame = frames; + const speech = `${artwork.title}, by ${artwork.artist}, ${artwork.date}`; + warmed.add(speech); + speakFn?.(speech, "jeffrey", "cloud", { volume: 1, provider: "jeffrey" }); + flash("I: MUSEUM RECORD"); +} + // ⌨️ Smart munch (space/enter): munch what you're on; otherwise step toward the // nearest confirmed-green square and munch it if you land on one — so rapid // presses chain through a run of greens for combos. @@ -1705,7 +1814,7 @@ function announceBoard() { } // 🎪 Act ───────────────────────────────────────────────────────────────────── -function act({ event: e, sound, speak, cursor, num: { randInt } }) { +function act({ event: e, sound, speak, cursor, net, num: { randInt } }) { snd = sound; // ⌨️/🖱️ Input mode: pressing a key hides the cursor + hover; moving the mouse // brings them back. @@ -1724,6 +1833,13 @@ function act({ event: e, sound, speak, cursor, num: { randInt } }) { announceBoard(); } + // 🏛️ Open the canonical museum object page for the last revealed work. + // Handle this before the generic end-screen "any key" advance. + if (mode === "art" && e.is("keyboard:down:i") && lastArtwork?.url) { + net?.web?.(lastArtwork.url, true); + return; + } + // Advance from end-of-board states (clear → next level, over/win → fresh game). const advance = () => { if (state === "clear") { @@ -2175,7 +2291,7 @@ function hdApi(layer) { return api; } -function paintGame({ wipe, ink, screen, write, box, line, text }) { +function paintGame({ wipe, ink, screen, write, box, line, text, paste }) { computeLayout(screen); wipe(...T.bg); paintBackground({ ink, box, screen }); @@ -2239,6 +2355,7 @@ function paintGame({ wipe, ink, screen, write, box, line, text }) { else if (cd.failed) bg = T.failedBg; else if (cd.known) bg = [40, 168, 80]; // confirmed-correct → whole square green else if (missed) bg = T.missedBg; // revealed missed answer = gray + else if (mode === "art") bg = dark ? [28, 31, 45] : [215, 218, 226]; else if (mode === "note") bg = noteTile(cd.letter); // pitch-class tint else if (colorWord) bg = colorWord; // color squares ARE that color else bg = tileColor(txt); @@ -2268,6 +2385,53 @@ function paintGame({ wipe, ink, screen, write, box, line, text }) { const colorWord = COLOR_WORDS[txt]; const missed = gameOver && cd.correct && !cd.eaten; + if (mode === "art") { + const bitmap = artImages.get(cd.artwork?.image); + const inset = max(2, round(cell * 0.06)); + const size = max(1, cell - inset * 2); + if (bitmap && bitmap !== "loading" && bitmap !== "failed" && paste) { + paste(bitmap, cx + inset, cy + inset, { width: size, height: size }); + } else { + // A tiny museum-frame placeholder while IIIF arrives (or if a source + // image disappears); the board remains playable either way. + const pulse = bitmap === "failed" ? 70 : round(70 + 25 * sin(frames * 0.12)); + ink(pulse, pulse, pulse + 12).box(cx + inset, cy + inset, size, size); + ink(pulse + 35).box( + cx + inset + round(size * 0.18), + cy + inset + round(size * 0.18), + max(1, round(size * 0.64)), + max(1, round(size * 0.64)), + "outline", + ); + } + + // State tint sits over the image so correctness remains legible without + // replacing the art. Missed works are revealed in gray at game over. + if (missed) ink(125, 130, 142, 145).box(cx + inset, cy + inset, size, size); + if (cd.known && !cd.failed) + ink(40, 190, 90, 105).box(cx + inset, cy + inset, size, size); + if (cd.failed) ink(195, 42, 52, 125).box(cx + inset, cy + inset, size, size); + if (cd.flash > 0) { + const tint = cd.correct ? [50, 220, 105] : [235, 55, 65]; + ink(tint[0], tint[1], tint[2], min(170, cd.flash * 10)).box( + cx + inset, + cy + inset, + size, + size, + ); + } + if (hover && hover.col === c && hover.row === r && state === "play") { + ink(...T.hoverOutline).box(cx + 1, cy + 1, cell - 2, cell - 2, "outline"); + ink(...T.hoverOutline).box(cx + 2, cy + 2, cell - 4, cell - 4, "outline"); + } + if (cd.failed) { + ink(255, 225, 225); + line(cx + 5, cy + 5, cx + cell - 5, cy + cell - 5); + line(cx + cell - 5, cy + 5, cx + 5, cy + cell - 5); + } + continue; + } + // Value — ONE color per answer (color-words use their hue + contrast), // centered in the cell. Word editions: unifont, whole word, scale 1. const font = cellFont(); @@ -2309,7 +2473,7 @@ function paintGame({ wipe, ink, screen, write, box, line, text }) { }; const mc = muncher.col, mr = muncher.row; - if (state === "play" && deathPhase === 0) { + if (mode !== "art" && state === "play" && deathPhase === 0) { if (mc === 0) wrapHint(COLS - 1, mr, x - cell, y + mr * cell); // wrap-left if (mc === COLS - 1) wrapHint(0, mr, x + COLS * cell, y + mr * cell); // wrap-right if (mr === 0) wrapHint(mc, ROWS - 1, x + mc * cell, y - cell); // wrap-up @@ -2453,9 +2617,17 @@ function paintGame({ wipe, ink, screen, write, box, line, text }) { // Let the celebration play out, then show the prompt overlay. if (state === "clear" && clearPhase === 0) - overlay({ ink, screen, write }, "LEVEL CLEAR", "press any key"); + overlay( + { ink, screen, write }, + "LEVEL CLEAR", + mode === "art" && lastArtwork ? "I: record · press to continue" : "press any key", + ); if (state === "over") - overlay({ ink, screen, write }, "GAME OVER", "press to retry"); + overlay( + { ink, screen, write }, + "GAME OVER", + mode === "art" && lastArtwork ? "I: record · press to retry" : "press to retry", + ); } // Cute monster: fatter the more it has eaten; shrinks + trembles when starving @@ -2619,6 +2791,7 @@ function paintTroggle({ ink, box, line }, cx, cy, cell, hue) { } function gameName() { + if (mode === "art") return "ARTNOM"; if (mode === "note") return "NOTENOM"; if (mode !== "word") return "NUMBNOM"; return lang === "es" ? "MEXINOM" : lang === "da" ? "DANNOM" : lang === "ru" ? "RUSNOM" : lang === "cat" ? "CATNOM" : "ENGNOM"; @@ -2626,6 +2799,7 @@ function gameName() { // Native edition subtitle, in each language's own script/diacritics. Rendered in // unifont (carries accents + Cyrillic) so the symbols are correct. function editionText() { + if (mode === "art") return "art history edition"; if (mode === "note") return "music edition"; if (mode !== "word") return "number edition"; return lang === "es" ? "edición mexicana" @@ -2683,18 +2857,24 @@ function overlay({ ink, screen, write }, title, sub) { // piece exports `meta() { return makeMeta([]); }`. function makeMeta(params) { const { mode: m, lang: l } = resolveMode(params); - const title = m === "note" + const title = m === "art" + ? "Artnom" + : m === "note" ? "Notenom" : m !== "word" ? "Numbnom" : l === "es" ? "Mexinom" : l === "da" ? "Dannom" : l === "ru" ? "Rusnom" : l === "cat" ? "Catnom" : "Engnom"; return { title, - desc: "Eat the squares that match the rule — numbnom (numbers), engnom (words), mexinom (español), dannom (dansk), rusnom (русский), notenom (notes), catnom (categories).", + desc: m === "art" + ? "Munch museum thumbnails by art-historical style — Getty AAT categories, IIIF images, and canonical object records." + : "Eat the squares that match the rule — numbnom (numbers), engnom (words), mexinom (español), dannom (dansk), rusnom (русский), notenom (notes), catnom (categories).", }; } function meta() { - return makeMeta([mode === "note" ? "notes" : mode === "word" ? lang : "numbers"]); + return makeMeta([ + mode === "art" ? "art" : mode === "note" ? "notes" : mode === "word" ? lang : "numbers", + ]); } export { boot, sim, paint, act, meta, makeMeta }; diff --git a/system/scripts/import-artnom.mjs b/system/scripts/import-artnom.mjs new file mode 100644 index 0000000000..ea82f69dcc --- /dev/null +++ b/system/scripts/import-artnom.mjs @@ -0,0 +1,180 @@ +#!/usr/bin/env node +// Build the small, rights-aware museum-object snapshot used by /artnom. +// +// Art Institute of Chicago is the first object source because its API exposes +// an explicit style taxonomy and IIIF images. Getty AAT URIs are the canonical +// round identities, matching AC's Linked Art records in backend/linked-art.mjs. +// Re-run from the repository root: +// node system/scripts/import-artnom.mjs + +// The generated manifest stores metadata + remote IIIF thumbnail URLs only; +// it does not copy museum image binaries into the repository. + +// 2026.07.21 + +import { mkdir, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const API = "https://api.artic.edu/api/v1"; +const IIIF = "https://www.artic.edu/iiif/2"; +const OUTPUT = fileURLToPath( + new URL("../public/aesthetic.computer/assets/artnom/manifest.json", import.meta.url), +); +const USER_AGENT = "Aesthetic Computer artnom (hello@aesthetic.computer)"; +const ARTWORKS_PER_CATEGORY = 10; +const MAX_PER_ARTIST = 3; + +// AIC has reconciled two of these terms directly to AAT. The remaining AAT +// ids were verified through Getty/Wikidata authority links on 2026-07-21. +const CATEGORIES = [ + { slug: "cubism", label: "Cubism", aatId: "300021495", aicTerm: "TM-7539" }, + { slug: "impressionism", label: "Impressionism", aatId: "300021503", aicTerm: "TM-7543" }, + { slug: "post-impressionism", label: "Post-Impressionism", aatId: "300021508", aicTerm: "TM-7547" }, + { slug: "surrealism", label: "Surrealism", aatId: "300021512", aicTerm: "TM-7551" }, + { slug: "pop-art", label: "Pop Art", aatId: "300022205", aicTerm: "TM-7221" }, +]; + +const FIELDS = [ + "id", + "title", + "artist_title", + "date_display", + "image_id", + "is_public_domain", + "style_title", + "style_ids", + "classification_title", + "thumbnail", +].join(","); + +async function json(url, options = {}) { + const response = await fetch(url, { + ...options, + headers: { "AIC-User-Agent": USER_AGENT, ...options.headers }, + }); + if (!response.ok) throw new Error(`${response.status} ${response.statusText}: ${url}`); + return response.json(); +} + +async function verifyTerm(category) { + const result = await json( + `${API}/category-terms/${encodeURIComponent(category.aicTerm)}` + + "?fields=id,title,subtype,aat_id", + ); + const term = result.data; + if (term?.title !== category.label || term?.subtype !== "style") { + throw new Error(`AIC term ${category.aicTerm} no longer resolves to ${category.label}`); + } + if (term.aat_id && String(term.aat_id) !== category.aatId) { + throw new Error( + `${category.label}: AIC now maps to AAT ${term.aat_id}, expected ${category.aatId}`, + ); + } +} + +async function fetchCategory(category) { + await verifyTerm(category); + const body = { + size: 100, + query: { + bool: { + must: [ + { term: { "style_title.keyword": category.label } }, + { exists: { field: "image_id" } }, + ], + }, + }, + }; + const rows = []; + for (let offset = 0; offset < 400; offset += 100) { + const result = await json( + `${API}/artworks/search?from=${offset}&size=100&fields=${FIELDS}`, + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }, + ); + rows.push(...(result.data || [])); + if (rows.length >= (result.pagination?.total || 0)) break; + } + + // Keep the API's relevance order, but prevent one artist from swallowing a + // whole round (particularly Picasso in Cubism and Warhol in Pop Art). + const artistCounts = new Map(); + const selected = []; + for (const work of rows) { + if (!work.image_id || !work.title || !work.artist_title) continue; + const artist = work.artist_title.trim(); + const count = artistCounts.get(artist) || 0; + if (count >= MAX_PER_ARTIST) continue; + artistCounts.set(artist, count + 1); + selected.push({ + id: `artic:${work.id}`, + source: "artic", + sourceId: String(work.id), + title: work.title, + artist, + date: work.date_display || "date unknown", + classification: work.classification_title || "artwork", + style: work.style_title, + sourceTerms: (work.style_ids || []).map((id) => `artic:${id}`), + aat: [`http://vocab.getty.edu/aat/${category.aatId}`], + publicDomain: work.is_public_domain === true, + rights: work.is_public_domain === true + ? "https://creativecommons.org/publicdomain/mark/1.0/" + : "http://rightsstatements.org/vocab/InC/1.0/", + image: `${IIIF}/${work.image_id}/full/200,/0/default.jpg`, + alt: work.thumbnail?.alt_text || `${work.title} by ${artist}`, + api: `${API}/artworks/${work.id}`, + url: `https://www.artic.edu/artworks/${work.id}`, + }); + if (selected.length >= ARTWORKS_PER_CATEGORY) break; + } + if (selected.length < ARTWORKS_PER_CATEGORY) { + throw new Error(`${category.label}: only found ${selected.length} usable artworks`); + } + return { + id: `aat:${category.aatId}`, + slug: category.slug, + label: category.label, + aatId: category.aatId, + aat: `http://vocab.getty.edu/aat/${category.aatId}`, + speech: category.label.toLowerCase(), + sourceTerms: [{ source: "artic", id: category.aicTerm, label: category.label }], + artworks: selected, + }; +} + +const categories = []; +for (const category of CATEGORIES) categories.push(await fetchCategory(category)); + +const manifest = { + schema: "https://aesthetic.computer/schemas/artnom-manifest-v1.json", + generatedAt: new Date().toISOString(), + license: "Museum metadata is CC0; image rights are recorded per object and images remain remote.", + sources: [ + { + id: "artic", + label: "Art Institute of Chicago", + api: API, + url: "https://www.artic.edu/open-access/public-api", + imageService: IIIF, + }, + { + id: "getty-aat", + label: "Getty Art & Architecture Thesaurus", + api: "https://vocab.getty.edu/sparql", + url: "https://www.getty.edu/research/tools/vocabularies/aat/", + }, + ], + categories, +}; + +await mkdir(dirname(OUTPUT), { recursive: true }); +await writeFile(OUTPUT, `${JSON.stringify(manifest, null, 2)}\n`); +console.log( + `Wrote ${OUTPUT}: ${categories.length} categories, ` + + `${categories.reduce((sum, category) => sum + category.artworks.length, 0)} artworks`, +); diff --git a/system/tests/artnom.test.mjs b/system/tests/artnom.test.mjs new file mode 100644 index 0000000000..f635060ce7 --- /dev/null +++ b/system/tests/artnom.test.mjs @@ -0,0 +1,85 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; + +import { + boot, + makeMeta, + paint, +} from "../public/aesthetic.computer/lib/nom.mjs"; + +const manifestUrl = new URL( + "../public/aesthetic.computer/assets/artnom/manifest.json", + import.meta.url, +); + +async function manifest() { + return JSON.parse(await readFile(manifestUrl, "utf8")); +} + +test("artnom manifest carries AAT identity, source records, IIIF, and rights", async () => { + const data = await manifest(); + assert.equal(data.categories.length, 5); + assert.equal(data.categories.flatMap((category) => category.artworks).length, 50); + for (const category of data.categories) { + assert.match(category.aat, /^http:\/\/vocab\.getty\.edu\/aat\/\d+$/); + assert.ok(category.artworks.length >= 10); + for (const artwork of category.artworks) { + assert.ok(artwork.aat.includes(category.aat)); + assert.match(artwork.image, /\/full\/200,\/0\/default\.jpg$/); + assert.match(artwork.url, /^https:\/\/www\.artic\.edu\/artworks\/\d+$/); + assert.ok(artwork.rights); + } + } +}); + +test("artnom boots the shared nom engine and paints image answers", async () => { + const data = await manifest(); + const pixels = new Uint8ClampedArray(8 * 6 * 4).fill(255); + let preloadCount = 0; + boot({ + params: ["art"], + artnom: data, + hud: { label() {} }, + clock: { resync() {} }, + net: { + preload: async () => { + preloadCount += 1; + return { img: { width: 8, height: 6, pixels } }; + }, + }, + num: { randInt: () => 0 }, + }); + await new Promise((resolve) => setImmediate(resolve)); + + const calls = { paste: 0, write: 0 }; + const chain = { + box() {}, + line() {}, + write() { calls.write += 1; }, + }; + paint({ + dark: true, + screen: { width: 900, height: 700 }, + wipe() {}, + ink() { return chain; }, + box() {}, + line() {}, + write() { calls.write += 1; }, + paste(bitmap, x, y, transform) { + calls.paste += 1; + assert.equal(bitmap.width, 64); + assert.equal(bitmap.height, 64); + assert.ok(Number.isFinite(x) && Number.isFinite(y)); + assert.ok(transform.width > 0 && transform.height > 0); + }, + }); + + assert.equal(preloadCount, 25); + assert.equal(calls.paste, 25); + assert.ok(calls.write > 0); + assert.deepEqual(makeMeta(["art"]), { + title: "Artnom", + desc: "Munch museum thumbnails by art-historical style — Getty AAT categories, IIIF images, and canonical object records.", + }); +});