Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

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

arena.mjs: add animated direction arrows + platform texturing + feet jump physics

- Created animated graphics for all directional arrow buttons (up/down/left/right)
with normal and active states that brighten and thicken when pressed
- Updated button painting to use animation buffers for directional arrows like jump/crouch
- Textured platform bottom with darker blue-black color
- Each platform side now has distinct color (north=brown, south=teal, east=red-brown, west=olive)
- Removed striped pattern from platform sides for cleaner look
- Removed platformEdge rendering (black outline on arena perimeter)
- Fixed feet wireframe to move with player during jump while circle stays planted on ground

graph.mjs: fix Math.floor for scroll/flip boundary calculations

- Use floor consistently for all mask boundaries to avoid 1-pixel artifacts
- Mixed ceil/floor caused floating-point arithmetic misalignment
- Fixes scroll and flip visual skip artifacts

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

+148 -40
+136 -36
system/public/aesthetic.computer/disks/arena.mjs
··· 447 447 line(30, 20, 32, 23); 448 448 }); 449 449 450 - // Up arrow button 451 - buttonBuffers.up = painting(28, 28, (api) => { 450 + // Up arrow button - normal state 451 + buttonBuffers.up_normal = painting(28, 28, (api) => { 452 452 const { wipe, ink, line, box } = api; 453 453 wipe(60, 75, 95, 255); // Blue background 454 454 ink(200, 220, 255); ··· 458 458 line(10, 14, 14, 6); // Left point 459 459 line(18, 14, 14, 6); // Right point 460 460 461 - // Decorative dots (use small boxes) 461 + // Decorative dots 462 462 ink(150, 200, 255); 463 463 box(10, 22, 1, 1); 464 464 box(18, 22, 1, 1); 465 465 }); 466 466 467 - // Down arrow button 468 - buttonBuffers.down = painting(28, 28, (api) => { 467 + // Up arrow button - active state (brightened) 468 + buttonBuffers.up_active = painting(28, 28, (api) => { 469 + const { wipe, ink, line, box } = api; 470 + wipe(100, 130, 170, 255); // Brighter blue 471 + ink(255, 255, 255); 472 + 473 + // Arrow pointing up (thicker with extra lines) 474 + line(14, 22, 14, 6); // Stem 475 + line(13, 22, 13, 6); 476 + line(15, 22, 15, 6); 477 + line(10, 14, 14, 4); // Left point extended 478 + line(18, 14, 14, 4); // Right point extended 479 + line(10, 15, 14, 5); 480 + line(18, 15, 14, 5); 481 + 482 + // Decorative dots highlighted 483 + ink(255, 255, 200); 484 + box(10, 22, 1, 1); 485 + box(18, 22, 1, 1); 486 + box(10, 23, 1, 1); 487 + box(18, 23, 1, 1); 488 + }); 489 + 490 + // Down arrow button - normal state 491 + buttonBuffers.down_normal = painting(28, 28, (api) => { 469 492 const { wipe, ink, line, box } = api; 470 493 wipe(60, 75, 95, 255); 471 494 ink(200, 220, 255); ··· 481 504 box(18, 6, 1, 1); 482 505 }); 483 506 484 - // Left arrow button 485 - buttonBuffers.left = painting(28, 28, (api) => { 507 + // Down arrow button - active state (brightened) 508 + buttonBuffers.down_active = painting(28, 28, (api) => { 509 + const { wipe, ink, line, box } = api; 510 + wipe(100, 130, 170, 255); // Brighter blue 511 + ink(255, 255, 255); 512 + 513 + // Arrow pointing down (thicker with extra lines) 514 + line(14, 6, 14, 24); // Stem extended 515 + line(13, 6, 13, 24); 516 + line(15, 6, 15, 24); 517 + line(10, 14, 14, 24); // Left point extended 518 + line(18, 14, 14, 24); // Right point extended 519 + line(10, 13, 14, 23); 520 + line(18, 13, 14, 23); 521 + 522 + // Decorative dots highlighted 523 + ink(255, 255, 200); 524 + box(10, 6, 1, 1); 525 + box(18, 6, 1, 1); 526 + box(10, 5, 1, 1); 527 + box(18, 5, 1, 1); 528 + }); 529 + 530 + // Left arrow button - normal state 531 + buttonBuffers.left_normal = painting(28, 28, (api) => { 486 532 const { wipe, ink, line, box } = api; 487 533 wipe(60, 75, 95, 255); 488 534 ink(200, 220, 255); ··· 498 544 box(22, 18, 1, 1); 499 545 }); 500 546 501 - // Right arrow button 502 - buttonBuffers.right = painting(28, 28, (api) => { 547 + // Left arrow button - active state (brightened) 548 + buttonBuffers.left_active = painting(28, 28, (api) => { 549 + const { wipe, ink, line, box } = api; 550 + wipe(100, 130, 170, 255); // Brighter blue 551 + ink(255, 255, 255); 552 + 553 + // Arrow pointing left (thicker with extra lines) 554 + line(22, 14, 4, 14); // Stem extended 555 + line(22, 13, 4, 13); 556 + line(22, 15, 4, 15); 557 + line(14, 10, 4, 14); // Top point extended 558 + line(14, 18, 4, 14); // Bottom point extended 559 + line(15, 10, 5, 14); 560 + line(15, 18, 5, 14); 561 + 562 + // Decorative dots highlighted 563 + ink(255, 255, 200); 564 + box(22, 10, 1, 1); 565 + box(22, 18, 1, 1); 566 + box(23, 10, 1, 1); 567 + box(23, 18, 1, 1); 568 + }); 569 + 570 + // Right arrow button - normal state 571 + buttonBuffers.right_normal = painting(28, 28, (api) => { 503 572 const { wipe, ink, line, box } = api; 504 573 wipe(60, 75, 95, 255); 505 574 ink(200, 220, 255); ··· 513 582 ink(150, 200, 255); 514 583 box(6, 10, 1, 1); 515 584 box(6, 18, 1, 1); 585 + }); 586 + 587 + // Right arrow button - active state (brightened) 588 + buttonBuffers.right_active = painting(28, 28, (api) => { 589 + const { wipe, ink, line, box } = api; 590 + wipe(100, 130, 170, 255); // Brighter blue 591 + ink(255, 255, 255); 592 + 593 + // Arrow pointing right (thicker with extra lines) 594 + line(6, 14, 24, 14); // Stem extended 595 + line(6, 13, 24, 13); 596 + line(6, 15, 24, 15); 597 + line(14, 10, 24, 14); // Top point extended 598 + line(14, 18, 24, 14); // Bottom point extended 599 + line(13, 10, 23, 14); 600 + line(13, 18, 23, 14); 601 + 602 + // Decorative dots highlighted 603 + ink(255, 255, 200); 604 + box(6, 10, 1, 1); 605 + box(6, 18, 1, 1); 606 + box(5, 10, 1, 1); 607 + box(5, 18, 1, 1); 516 608 }); 517 609 518 610 console.log("✓ Button buffers created successfully"); ··· 615 707 // 🧱 Platform block — bottom and side faces to give the arena volume 616 708 const platformDepth = 2.0; // thickness of the platform block 617 709 const bottomY = GROUND_Y - platformDepth; 618 - const sideColor = [0.18, 0.16, 0.14, 1.0]; // darker than ground for depth 619 - const sideStriped = [0.28, 0.25, 0.22, 1.0]; // lighter stripe for pattern 710 + 711 + // Different colors for each side 712 + const bottomColor = [0.08, 0.08, 0.12, 1.0]; // Dark blue-black texture 713 + const northColor = [0.22, 0.18, 0.14, 1.0]; // Brown 714 + const southColor = [0.16, 0.20, 0.18, 1.0]; // Dark teal 715 + const eastColor = [0.20, 0.16, 0.16, 1.0]; // Dark red-brown 716 + const westColor = [0.18, 0.20, 0.14, 1.0]; // Dark olive 717 + 620 718 const platformGs = GROUND_SIZE; 621 719 622 720 const platformPositions = []; 623 721 const platformColors = []; 624 722 625 - // Bottom face (two triangles, full area) 723 + // Bottom face (two triangles, full area) - textured 626 724 platformPositions.push( 627 725 [-platformGs, bottomY, -platformGs, 1], [-platformGs, bottomY, platformGs, 1], [platformGs, bottomY, platformGs, 1], 628 726 [-platformGs, bottomY, -platformGs, 1], [platformGs, bottomY, platformGs, 1], [platformGs, bottomY, -platformGs, 1], 629 727 ); 630 - for (let i = 0; i < 6; i++) platformColors.push(sideColor); 728 + for (let i = 0; i < 6; i++) platformColors.push(bottomColor); 631 729 632 - // Side faces with alternating stripe pattern for visual interest 633 - const sideStep = (platformGs * 2) / 8; // stripe width 730 + // Side faces - solid colors per side (no stripes) 731 + const sideStep = (platformGs * 2) / 8; 634 732 635 - // North side (-Z direction) 733 + // North side (-Z direction) - brown 636 734 for (let i = 0; i < 8; i++) { 637 735 const x0 = -platformGs + i * sideStep; 638 736 const x1 = x0 + sideStep; 639 - const stripeColor = (i & 1) ? sideStriped : sideColor; 640 737 platformPositions.push( 641 738 [x0, GROUND_Y, -platformGs, 1], [x0, bottomY, -platformGs, 1], [x1, bottomY, -platformGs, 1], 642 739 [x0, GROUND_Y, -platformGs, 1], [x1, bottomY, -platformGs, 1], [x1, GROUND_Y, -platformGs, 1], 643 740 ); 644 - for (let j = 0; j < 6; j++) platformColors.push(stripeColor); 741 + for (let j = 0; j < 6; j++) platformColors.push(northColor); 645 742 } 646 743 647 - // South side (+Z direction) 744 + // South side (+Z direction) - teal 648 745 for (let i = 0; i < 8; i++) { 649 746 const x0 = -platformGs + i * sideStep; 650 747 const x1 = x0 + sideStep; 651 - const stripeColor = (i & 1) ? sideStriped : sideColor; 652 748 platformPositions.push( 653 749 [x0, GROUND_Y, platformGs, 1], [x1, bottomY, platformGs, 1], [x1, GROUND_Y, platformGs, 1], 654 750 [x0, GROUND_Y, platformGs, 1], [x0, bottomY, platformGs, 1], [x1, bottomY, platformGs, 1], 655 751 ); 656 - for (let j = 0; j < 6; j++) platformColors.push(stripeColor); 752 + for (let j = 0; j < 6; j++) platformColors.push(southColor); 657 753 } 658 754 659 - // East side (+X direction) 755 + // East side (+X direction) - red-brown 660 756 for (let i = 0; i < 8; i++) { 661 757 const z0 = -platformGs + i * sideStep; 662 758 const z1 = z0 + sideStep; 663 - const stripeColor = (i & 1) ? sideStriped : sideColor; 664 759 platformPositions.push( 665 760 [platformGs, GROUND_Y, z0, 1], [platformGs, bottomY, z0, 1], [platformGs, bottomY, z1, 1], 666 761 [platformGs, GROUND_Y, z0, 1], [platformGs, bottomY, z1, 1], [platformGs, GROUND_Y, z1, 1], 667 762 ); 668 - for (let j = 0; j < 6; j++) platformColors.push(stripeColor); 763 + for (let j = 0; j < 6; j++) platformColors.push(eastColor); 669 764 } 670 765 671 - // West side (-X direction) 766 + // West side (-X direction) - olive 672 767 for (let i = 0; i < 8; i++) { 673 768 const z0 = -platformGs + i * sideStep; 674 769 const z1 = z0 + sideStep; 675 - const stripeColor = (i & 1) ? sideStriped : sideColor; 676 770 platformPositions.push( 677 771 [-platformGs, GROUND_Y, z0, 1], [-platformGs, bottomY, z1, 1], [-platformGs, GROUND_Y, z1, 1], 678 772 [-platformGs, GROUND_Y, z0, 1], [-platformGs, bottomY, z0, 1], [-platformGs, bottomY, z1, 1], 679 773 ); 680 - for (let j = 0; j < 6; j++) platformColors.push(stripeColor); 774 + for (let j = 0; j < 6; j++) platformColors.push(westColor); 681 775 } 682 776 683 777 platformBlock = new Form( ··· 1043 1137 } 1044 1138 1045 1139 if (bodyFeet) { 1046 - const footBaseY = playerAlive 1047 - ? GROUND_Y 1048 - : playerWorldY - EYE_HEIGHT; 1140 + // Feet move with player when airborne, stay planted when on ground 1141 + const phys = system?.fps?.doll?.physics; 1142 + let footY; 1143 + if (playerAlive) { 1144 + // When on ground, feet are at ground level 1145 + // When in air, feet move with player (showing the jump height) 1146 + footY = (phys?.onGround) ? GROUND_Y : playerWorldY; 1147 + } else { 1148 + footY = playerWorldY - EYE_HEIGHT; 1149 + } 1049 1150 bodyFeet.position[0] = playerCamX; 1050 - bodyFeet.position[1] = footBaseY; 1151 + bodyFeet.position[1] = footY; 1051 1152 bodyFeet.position[2] = playerCamZ; 1052 1153 bodyFeet.rotation[1] = playerFacing; 1053 1154 } ··· 1198 1299 if (platformBlock) ink(255).form(platformBlock); // bottom and side faces 1199 1300 if (groundSkirt) ink(255).form(groundSkirt); 1200 1301 ink(255).form(groundPlane); 1201 - if (platformEdge) ink(255).form(platformEdge); 1202 1302 if (hiPos.length > 0 && FormRef) { 1203 1303 const hi = new FormRef( 1204 1304 { type: "triangle", positions: hiPos, colors: hiCol }, ··· 1432 1532 1433 1533 // Determine which buffer to render based on button state 1434 1534 let bufferName = name; 1435 - if (name === "jump" || name === "crouch") { 1436 - // Jump/Crouch buttons have animation states 1535 + if (name === "jump" || name === "crouch" || name === "up" || name === "down" || name === "left" || name === "right") { 1536 + // Jump/Crouch/Arrow buttons have animation states 1437 1537 bufferName = isPressed ? `${name}_active` : `${name}_normal`; 1438 1538 if (name === "crouch") { 1439 1539 console.log(`🐒 crouch button: isPressed=${isPressed}, bufferName=${bufferName}, shift=${keyboardState.shift}`); ··· 1533 1633 if (e.key === "arrowleft") keyboardState.arrowleft = true; 1534 1634 if (e.key === "arrowright") keyboardState.arrowright = true; 1535 1635 if (e.key === " ") keyboardState.space = true; 1536 - if (e.key === "shift") keyboardState.shift = true; 1636 + if (e.key === "Shift") keyboardState.shift = true; 1537 1637 } else if (e.is("keyboard:up")) { 1538 1638 if (e.key === "w") keyboardState.w = false; 1539 1639 if (e.key === "a") keyboardState.a = false; ··· 1544 1644 if (e.key === "arrowleft") keyboardState.arrowleft = false; 1545 1645 if (e.key === "arrowright") keyboardState.arrowright = false; 1546 1646 if (e.key === " ") keyboardState.space = false; 1547 - if (e.key === "shift") keyboardState.shift = false; 1647 + if (e.key === "Shift") keyboardState.shift = false; 1548 1648 } 1549 1649 1550 1650 // 📱 Trigger button input handling
+12 -4
system/public/aesthetic.computer/lib/graph.mjs
··· 5457 5457 maxX = width, 5458 5458 maxY = height; 5459 5459 if (activeMask) { 5460 + // 🔧 FIX: Use floor for all boundaries to avoid 1-pixel artifacts 5461 + // When ceil is mixed with floor, floating-point arithmetic can cause the 5462 + // effective bounds to be 1 pixel off, creating scroll skip artifacts. 5463 + // Using floor consistently gives us the pixel-aligned inner bounds. 5460 5464 const maskMinX = Math.floor(activeMask.x); 5461 5465 const maskMinY = Math.floor(activeMask.y); 5462 - const maskMaxX = Math.ceil(activeMask.x + activeMask.width); 5463 - const maskMaxY = Math.ceil(activeMask.y + activeMask.height); 5466 + const maskMaxX = Math.floor(activeMask.x + activeMask.width); 5467 + const maskMaxY = Math.floor(activeMask.y + activeMask.height); 5464 5468 5465 5469 minX = Math.max(0, Math.min(width, maskMinX)); 5466 5470 maxX = Math.max(0, Math.min(width, maskMaxX)); ··· 5558 5562 maxX = width, 5559 5563 maxY = height; 5560 5564 if (activeMask) { 5565 + // 🔧 FIX: Use floor for all boundaries to avoid 1-pixel artifacts 5566 + // When ceil is mixed with floor, floating-point arithmetic can cause the 5567 + // effective bounds to be 1 pixel off, creating visual skip artifacts. 5568 + // Using floor consistently gives us the pixel-aligned inner bounds. 5561 5569 const maskMinX = Math.floor(activeMask.x); 5562 5570 const maskMinY = Math.floor(activeMask.y); 5563 - const maskMaxX = Math.ceil(activeMask.x + activeMask.width); 5564 - const maskMaxY = Math.ceil(activeMask.y + activeMask.height); 5571 + const maskMaxX = Math.floor(activeMask.x + activeMask.width); 5572 + const maskMaxY = Math.floor(activeMask.y + activeMask.height); 5565 5573 5566 5574 minX = Math.max(0, Math.min(width, maskMinX)); 5567 5575 maxX = Math.max(0, Math.min(width, maskMaxX));