Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

The Contraption Bazaar Sticky

A topic by Internet Janitor created Feb 28, 2023 Views: 3,719 Replies: 41
Viewing posts 1 to 20 of 22 · Next page · Last page
Developer (1 edit) (+6)

Seeking fancy new contraptions for your decks? Seek no further, friend! Have a look at my wares, ready to copy and paste right into Decker:

The "marquee" contraption:

Configure your choice of text and size! Respects the "font" setting for contraption instances.

%%WGT0{"w":[{"name":"marquee1","type":"contraption","size":[100,100],"pos":[391,195],"def":"marquee","widgets":{"canvas1":{},"text":{}}}],"d":{"marquee":{"name":"marquee","size":[100,100],"resizable":1,"margin":[5,5,5,5],"description":"A horizontally-scrolling text marquee.","script":"on set_text x do text.value:x end\non get_text do text.value end","attributes":{"name":["text"],"label":["Text"],"type":["rich"]},"widgets":{"canvas1":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"script":"on view do\n me.font:card.font\n t:me.font.textsize[text.text][0]\n w:me.size[0]+t\n me.clear[]\n me.text[text.text (me.size/1,2)-(w%sys.ms/10),0 \"center_left\"]\nend","scale":1},"text":{"type":"field","size":[27,25],"pos":[1,-57],"locked":1,"show":"none","value":"Your Text Here!"}}}}}

The "flag" contraption:

Add your favorite horizontally-striped flags to your deck, big or small, square or rectangular. The color list may use any of the built-in named colors ( whiteblackyelloworangeredmagentapurplebluecyangreendarkgreenbrowntanlightgraymediumgraydarkgray ) or pattern indices (0-47).

%%WGT0{"w":[{"name":"flag1","type":"contraption","size":[100,100],"pos":[206,121],"def":"flag","widgets":{"canv":{},"val":{}}}],"d":{"flag":{"name":"flag","size":[100,100],"resizable":1,"margin":[9,9,9,9],"description":"a resizable flag made of multicolored horizontal strips.","script":"on get_value do val.text end\non set_value x do val.text:x view[] end\non view do\n c:each c in \",\" split val.text\n  (0+c) unless colors[c]\n end\n canv.clear[]\n s:canv.size[1]/count c\n each v i in c\n  canv.pattern:v\n  canv.rect[0,s*i 1+canv.size[0],s] \n end\nend","attributes":{"name":["value"],"label":["Colors\n(Comma\nSeparated)"],"type":["code"]},"widgets":{"canv":{"type":"canvas","size":[100,100],"pos":[0,0],"pattern":37,"scale":1},"val":{"type":"field","size":[90,20],"pos":[1,-31],"locked":1,"border":0,"style":"plain","align":"center","value":"red,orange,yellow,green,blue,purple"}}}}}

The "patternPicker" contraption:


A grid of swatches- click one to choose a pattern and fire a change[] event! Configurable to include or exclude colors. If you're looking for a little scripting exercise, try using a patternPicker to make a fancier editor for the flag contraption!

%%WGT0{"w":[{"name":"patternPicker1","type":"contraption","size":[100,100],"pos":[48,107],"def":"patternPicker","widgets":{"c":{},"v":{},"cc":{}}}],"d":{"patternPicker":{"name":"patternPicker","size":[100,100],"resizable":1,"margin":[10,10,10,10],"description":"a palette of patterns and (optionally) colors.","script":"on get_value do v.value end\non set_value x do v.value:x view[] end\non get_color do cc.value end\non set_color x do cc.value:x view[] end\n\nb:5\nr:4+2*cc.value\ns:floor(c.size-2*b)/8,r\n\non boxes do\n flip b+s*flip 8 cross r\nend\n\non view do\n o:-50\n c.clear[]\n each p i in boxes[]\n  c.pattern:i\n  c.rect[p s]\n  c.pattern:1\n  c.box[p s+1]\n  if i=v.value o:p end\n end\n c.pattern:0\n c.box[o-1 s+3]\n c.pattern:1\n c.box[o-2 s+5]\nend\n\non inside p r do\n min(p>r)&(r+s>p)\nend\non click pos do\n each p i in boxes[]\n  if inside[pos p]\n   set_value[i]\n   card.event[\"change\" i]\n  end\n end\nend\n","template":"on change val do\n \nend","attributes":{"name":["value","color"],"label":["Value","Color"],"type":["number","bool"]},"widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"border":0,"scale":1},"v":{"type":"slider","size":[60,25],"pos":[2,-41],"locked":1,"show":"none","interval":[0,47]},"cc":{"type":"button","size":[14,20],"pos":[73,-40],"locked":1,"show":"none","style":"check","value":1}}}}}

Have some contraptions of your own to share? There's plenty of room in the Bazaar!

Developer (1 edit) (+1)

How about,

The "DVD ScreenSaver" contraption:


Gracefully sliding around the card of your choice forever! Includes a "bump" event fired every time it bounces off an edge of the screen. If you edit the prototype, you can make this use whatever image you like.

%%WGT0{"w":[{"name":"dvdScreenSaver1","type":"contraption","size":[101,58],"pos":[192,134],"show":"transparent","def":"dvdScreenSaver","widgets":{"vel":{"value":"[-3,-2]"}}}],"d":{"dvdScreenSaver":{"name":"dvdScreenSaver","size":[101,58],"margin":[0,0,0,0],"description":"just the dvd screen saver.","script":"on set_vel x do vel.text:\"%j\" format list x end\non get_vel do \"%j\" parse vel.text end\n\non view do\n v:get_vel[]\n p:v+card.pos\n s:(512,342)-card.size\n if !p[0]>0    v[0]:-v[0] b:1 end\n if !p[1]>0    v[1]:-v[1] b:1 end\n if  p[0]>s[0] v[0]:-v[0] b:1 end\n if  p[1]>s[1] v[1]:-v[1] b:1 end\n card.pos:(s-1)&0|p\n set_vel[v]\n if b card.event[\"bump\"] end\nend\n","template":"on bump do\n \nend","image":"%%IMG0AGUAOgB//////AAA////gAAAf/////wAAf////gAAP/////+AAP////+AAD//////gAH/////4AA//////4AB//////AAP//////AA//////4AAAAH///wAf+AAB/+AAAAAf//8AP/AAAH/wAf+AD///gH/n/gA/8AH/gA/+/4D/z/wAP/gB/wAH/v+A/8/8AB/4A/8AB/7/gf+P/AAf+AP/AAf+f8P/D/wAH/gD/wAP/n/H/g/4AD/wB/4AD/5/z/wf+AA/8Af+AB/8P+/4H/gAf/AH/gA//D//8B/4AP/gB/wA//gf/+Af8AP/wA/8A//wH//AP/AP/8AP/B//4B//wD/x//+AD////4AP/4A////+AA////8AD/8AP////AAP///4AA/+AH////AAH///gAAH/AB///+AAB//8AAAB/gAf//8AAAAAAAAAAfwAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAP/////+AAAAAAAAA//////////AAAAAA////////////8AAAD/////////////8AAH/////+AAf/////4AH/////AAAAP/////gD/////gAAAB/////4Af////+AAAA/////+AD/////4AAB//////AAP//////B///////AAAH////////////4AAAAD///////////AAAAAAAP////////AAdsAAAAAAf////AAAACVAAAAAAAAAAAAAAAAkQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwGBwP4Af+AHwAAAAAODgcD/gH/gH/AAAAADhwHAw8BwADg4AAAAAccBwMHgcABwHAAAAADOAcDA4H/AYAwAAAAA7AHAwOB/wGAMAAAAAHwBwMHgcABwHAAAAAB4AcDDwHAAODgAAAAAOAHA/4B/4B/wAAAAADABwP4Af+AHwAAAA=","attributes":{"name":[],"label":[],"type":[]},"widgets":{"vel":{"type":"field","size":[37,13],"pos":[2,-19],"locked":1,"animated":1,"show":"none","style":"plain","align":"center","value":"[3,2]"}}}}}
(+3)

ur a legend

Developer

Thank you. :)

(+2)

sorry, i think I must have missed something. how should I paste these into a deck?

Developer (1 edit) (+3)

Copy the code starting at the "%%WGT" part and ending with the last "}" to your clipboard, open Decker, and then choose "Edit -> Paste Widgets" (or just "Edit -> Paste" in web-decker) from the main menu.

The code blocks on this page are what it looks like when you copy and paste one or more widgets in Decker. When you copy a Contraption to the clipboard, it carries along the associated Prototype (contraption definition), which makes it possible to share them like this or easily copy and paste them between decks. The same idea applies to copying and pasting cards.

Contraptions were added in v1.12, so if you downloaded an older version of Decker, please make sure you upgrade!

Does that clear things up?

(+1)

yes that absolutely does! thank you for being so thorough in your reply! i was also tripped up by needing to leave the widget tool for them to start doing their thing, but that one's on me

Developer (1 edit) (+1)

A potentially more generally useful variant of the DVD ScreenSaver Contraption,

The Bouncer Contraption:



Same basic idea as its predecessor, but now you can paste a custom image into each instance, and that image will be bounced within the boundary of the contraption's (resizable) bounding box.

%%WGT0{"w":[{"name":"bouncer1","type":"contraption","size":[260,188],"pos":[126,68],"def":"bouncer","widgets":{"c":{"size":[101,58],"pos":[42,53]},"i":{"size":[100,32],"pos":[280,-6]},"p":{"size":[100,38],"pos":[280,45],"value":"{\"v\":[3,-2],\"p\":[42,53]}"}}}],"d":{"bouncer":{"name":"bouncer","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"bounce an image around within a bounding rectangle","script":"on get_img     do i.value   end\non set_img x   do i.value:x end\non get_state   do p.text    end\non set_state x do p.text:x  end\n\non view do\n img:first extract arg where arg..type=\"image\" from i.value\n s:\"%j\" parse p.text\n b:card.size-img.size\n if !\"v\" in s  s.v:3,2 end\n if !\"p\" in s  s.p:b/2 end\n \n s.p:s.p+s.v\n if !s.p[0]>0    s.v[0]:-s.v[0] end\n if !s.p[1]>0    s.v[1]:-s.v[1] end\n if  s.p[0]>b[0] s.v[0]:-s.v[0] end\n if  s.p[1]>b[1] s.v[1]:-s.v[1] end\n s.p:(b-1)&0|s.p\n p.text:\"%j\" format s\n \n c.size:img.size\n c.pos:s.p\n c.clear[]\n c.paste[img 0,0 1]\nend\n","attributes":{"name":["img","state"],"label":["Image","State"],"type":["rich","string"]},"widgets":{"c":{"type":"canvas","size":[36,35],"pos":[120,53],"locked":1,"animated":1,"show":"transparent","border":0,"scale":1},"i":{"type":"field","size":[100,20],"pos":[120,-6],"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG0AGUAOgB//////AAA////gAAAf/////wAAf////gAAP/////+AAP////+AAD//////gAH/////4AA//////4AB//////AAP//////AA//////4AAAAH///wAf+AAB/+AAAAAf//8AP/AAAH/wAf+AD///gH/n/gA/8AH/gA/+/4D/z/wAP/gB/wAH/v+A/8/8AB/4A/8AB/7/gf+P/AAf+AP/AAf+f8P/D/wAH/gD/wAP/n/H/g/4AD/wB/4AD/5/z/wf+AA/8Af+AB/8P+/4H/gAf/AH/gA//D//8B/4AP/gB/wA//gf/+Af8AP/wA/8A//wH//AP/AP/8AP/B//4B//wD/x//+AD////4AP/4A////+AA////8AD/8AP////AAP///4AA/+AH////AAH///gAAH/AB///+AAB//8AAAB/gAf//8AAAAAAAAAAfwAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAP/////+AAAAAAAAA//////////AAAAAA////////////8AAAD/////////////8AAH/////+AAf/////4AH/////AAAAP/////gD/////gAAAB/////4Af////+AAAA/////+AD/////4AAB//////AAP//////B///////AAAH////////////4AAAAD///////////AAAAAAAP////////AAdsAAAAAAf////AAAACVAAAAAAAAAAAAAAAAkQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwGBwP4Af+AHwAAAAAODgcD/gH/gH/AAAAADhwHAw8BwADg4AAAAAccBwMHgcABwHAAAAADOAcDA4H/AYAwAAAAA7AHAwOB/wGAMAAAAAHwBwMHgcABwHAAAAAB4AcDDwHAAODgAAAAAOAHA/4B/4B/wAAAAADABwP4Af+AHwAAAA="]}},"p":{"type":"field","size":[100,20],"pos":[120,24],"show":"none","style":"plain"}}}}}
Developer (1 edit) (+1)

Introducing

The "macWindow" contraption:

A resizable window frame that resembles System 6.

If you make the contraption transparent, you can draw underneath the frame. If you leave it "solid", you can overlap it on other windows. The title is configurable, and "close" and "resize" events are fired by clicking on the corner buttons.

While this contraption is not a "true" window, it can be a handy decoration or the beginnings of a more realistic simulacrum.

%%WGT0{"w":[{"name":"win","type":"contraption","size":[59,71],"pos":[211,143],"def":"macWindow","widgets":{"close":{},"resize":{},"title":{}}}],"d":{"macWindow":{"name":"macWindow","size":[59,71],"resizable":1,"margin":[22,34,33,33],"description":"a window frame in the style of MacOS 6.","script":"on get_title do title.text end\non set_title x do title.text:x end\n\non view do\n t:get_title[]\n if count t\n  title.font:\"menu\"\n  s:first title.font.textsize[t]\n  title.size:(s+10),title.size[1]\n  title.pos :(.5*card.size[0]-title.size[0]),title.pos[1]\n  title.show:\"solid\"\n else\n  title.show:\"none\"\n end\nend","template":"on close do\n \nend\n\non resize do\n \nend","image":"%%IMG2ADsARwE8IDgBAyA4AQMgOAEDIAEBBiABAQsgAQEQIAEBCyABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQcgAwEBIAEBBiABAQMgCAEBIAkBASASAQEgCQEBIAgBAyABAQYgAQEBIAkBASABARAgAQEBIAkBASABAQYgAQEDIAgBASAJAQEgEgEBIAkBASAIAQMgAQEGIAEBCyABARAgAQELIAEBBiABAQMgOAEDIDgBAyA4AT4AKQEBIA4BAwApAQEgBgEBIAcBAwApAQEgBQEBIAEBASAGAQMAKQEBIAQBASADAQEgBQEDACkBASADAQEgBQEBIAQBAwApAQEgAgEBIAcBASADAQMAKQEBIAEBASAJAQEgAgEDACkBBSAFAQQgAQEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQcgBAEDACkBASAOAQMAKQEBIA4BAwApARIAKQEBIA4BAwApAQEgDgEDACkBASAOAQMAKQEBIA4BAwApAQEgDgEDACkBEgApAQEgDgEDACkBASAOAQMAKQEBIAMBByAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEFIAUBBCABAQMAKQEBIAEBASAJAQEgAgEDACkBASACAQEgBwEBIAMBAwApAQEgAwEBIAUBASAEAQMAKQEBIAQBASADAQEgBQEDACkBASAFAQEgAQEBIAYBAwApAQEgBgEBIAcBAwApAQEgDgE+IAcBASAGAQEgCwEBIAYBASAHAQEgDgEDIAYBAiAGAQEgCwEBIAYBAiAGAQEgDgEDIAUBASABAQEgBgEBIAsBASAGAQEgAQEBIAUBASACAQcgBQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASACAQEgBQEBIAUBAyADAQEgBwEBIAIBASALAQEgAgEBIAcBASADAQEgAgEBIAUBBSABAQMgAgEBIAgBASACAQEgCwEBIAIBASAIAQEgAgEBIAIBASAFAQEgAwEBIAEBAyABAQEgCQEBIAIBASALAQEgAgEBIAkBASABAQEgAgEBIAUBASADAQEgAQEDIAIBASAIAQEgAgEBIAsBASACAQEgCAEBIAIBASACAQEgBQEBIAMBASABAQMgAwEBIAcBASACAQEgCwEBIAIBASAHAQEgAwEBIAIBByADAQEgAQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASAEAQEgBwEBIAEBAyAFAQEgAQEBIAYBASALAQEgBgEBIAEBASAFAQEgBAEBIAcBASABAQMgBgECIAYBASALAQEgBgECIAYBASAEAQEgBwEBIAEBAyAHAQEgBgEBIAsBASAGAQEgBwEBIAQBCSABAQMgDgEBIAsBASAOAQEgDgE9AAEBOg==","attributes":{"name":["title"],"label":["Title"],"type":["string"]},"widgets":{"close":{"type":"button","size":[11,11],"pos":[9,4],"script":"on click do\n card.event[\"close\"]\nend","style":"invisible"},"resize":{"type":"button","size":[11,11],"pos":[38,4],"script":"on click do\n card.event[\"resize\"]\nend","style":"invisible"},"title":{"type":"field","size":[44,17],"pos":[-50,1],"locked":1,"font":"menu","show":"none","border":0,"style":"plain","align":"center"}}}}}
(+3)


I introduced Interact dragging and resizing to macWindow, and included a rich text field to demonstrate how widgets can be embedded in such contraptions!

%%WGT0{"w":[{"name":"macFieldWindow1","type":"contraption","size":[99,92],"pos":[355,214],"def":"macFieldWindow","widgets":{"close":{},"resize":{"pos":[78,4]},"title":{"size":[37,17],"pos":[31,1],"value":"Hello"},"drag_resize":{"pos":[83,76]},"field":{"size":[98,58],"value":{"text":["I am a draggable and resizeable window sporting a rich text field!\n","Try me!"],"font":["","menu"],"arg":["",""]}},"bar":{"size":[99,19],"image":"%%IMG2AGMAEwD/AP8A/wD/AP8A/wD/AGA="}}}],"d":{"macFieldWindow":{"name":"macFieldWindow","size":[59,71],"resizable":1,"margin":[22,34,33,33],"description":"a window frame in the style of MacOS 6, sporting a field as well as supporting dragging and resizing! The internal field is available  at x.field","script":"on get_title do title.text end\non set_title x do title.text:x end\non get_field do field end\non get_value do field.value end\non set_value x do field.value: x end\n\non view do\n field.locked:card.locked\n t:get_title[]\n if count t\n  title.font:\"menu\"\n  s:first title.font.textsize[t]\n  title.size:(s+10),title.size[1]\n  title.pos :(.5*card.size[0]-title.size[0]),title.pos[1]\n  title.show:\"solid\"\n else\n  title.show:\"none\"\n end\nend","template":"on close do\n \nend\n\non resize do\n \nend","image":"%%IMG2ADsARwE8IDgBAyA4AQMgOAEDIAEBBiABAQsgAQEQIAEBCyABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQEgBQEBIAMBASABAQYgAQEDIAgBASAJAQEgEgEBIAUBASADAQEgCAEDIAEBBiABAQEgCQEBIAEBECABAQcgAwEBIAEBBiABAQMgCAEBIAkBASASAQEgCQEBIAgBAyABAQYgAQEBIAkBASABARAgAQEBIAkBASABAQYgAQEDIAgBASAJAQEgEgEBIAkBASAIAQMgAQEGIAEBCyABARAgAQELIAEBBiABAQMgOAEDIDgBAyA4AT4AKQEBIA4BAwApAQEgBgEBIAcBAwApAQEgBQEBIAEBASAGAQMAKQEBIAQBASADAQEgBQEDACkBASADAQEgBQEBIAQBAwApAQEgAgEBIAcBASADAQMAKQEBIAEBASAJAQEgAgEDACkBBSAFAQQgAQEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQcgBAEDACkBASAOAQMAKQEBIA4BAwApARIAKQEBIA4BAwApAQEgDgEDACkBASAOAQMAKQEBIA4BAwApAQEgDgEDACkBEgApAQEgDgEDACkBASAOAQMAKQEBIAMBByAEAQMAKQEBIAMBASAFAQEgBAEDACkBASADAQEgBQEBIAQBAwApAQEgAwEBIAUBASAEAQMAKQEFIAUBBCABAQMAKQEBIAEBASAJAQEgAgEDACkBASACAQEgBwEBIAMBAwApAQEgAwEBIAUBASAEAQMAKQEBIAQBASADAQEgBQEDACkBASAFAQEgAQEBIAYBAwApAQEgBgEBIAcBAwApAQEgDgE+IAcBASAGAQEgCwEBIAYBASAHAQEgDgEDIAYBAiAGAQEgCwEBIAYBAiAGAQEgDgEDIAUBASABAQEgBgEBIAsBASAGAQEgAQEBIAUBASACAQcgBQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASACAQEgBQEBIAUBAyADAQEgBwEBIAIBASALAQEgAgEBIAcBASADAQEgAgEBIAUBBSABAQMgAgEBIAgBASACAQEgCwEBIAIBASAIAQEgAgEBIAIBASAFAQEgAwEBIAEBAyABAQEgCQEBIAIBASALAQEgAgEBIAkBASABAQEgAgEBIAUBASADAQEgAQEDIAIBASAIAQEgAgEBIAsBASACAQEgCAEBIAIBASACAQEgBQEBIAMBASABAQMgAwEBIAcBASACAQEgCwEBIAIBASAHAQEgAwEBIAIBByADAQEgAQEDIAQBASACAQUgAgEBIAsBASACAQUgAgEBIAQBASAEAQEgBwEBIAEBAyAFAQEgAQEBIAYBASALAQEgBgEBIAEBASAFAQEgBAEBIAcBASABAQMgBgECIAYBASALAQEgBgECIAYBASAEAQEgBwEBIAEBAyAHAQEgBgEBIAsBASAGAQEgBwEBIAQBCSABAQMgDgEBIAsBASAOAQEgDgE9AAEBOg==","attributes":{"name":["title","value"],"label":["Title","Text"],"type":["string","rich"]},"widgets":{"close":{"type":"button","size":[11,11],"pos":[9,4],"script":"on click do\n card.event[\"close\"]\nend","style":"invisible"},"resize":{"type":"button","size":[11,11],"pos":[38,4],"script":"on click do\n card.event[\"resize\"]\nend","style":"invisible"},"title":{"type":"field","size":[8,17],"pos":[-12,1],"locked":1,"font":"menu","show":"none","border":0,"style":"plain","align":"center"},"drag_resize":{"type":"canvas","size":[14,14],"pos":[43,55],"script":"on click pos do\n field.locked:1\nend\n\non drag pos do\n card.size:(me.offset-card.pos-16)|(title.size[0]+57),85\n view[]\n field.locked:1\nend\n\non release pos do\n field.locked:card.locked\nend","show":"transparent","border":0,"image":"%%IMG2AA4ADgDE","draggable":1,"scale":1},"field":{"type":"field","size":[58,37],"pos":[0,18],"border":1,"scrollbar":1},"bar":{"type":"canvas","size":[59,19],"pos":[0,0],"script":"on click pos do\n field.locked:1\nend\n\non drag pos do\n card.pos:me.offset\nend\n\non release pos do\n field.locked:card.locked\nend","show":"transparent","border":0,"image":"%%IMG2ADsAEwD/AP8A/wD/AGU=","draggable":1,"scale":1}}}}}
Developer(+2)

Now Serving,

The EggTimer Contraption

A countdown timer for a configurable number of seconds, firing a "finish[]" event and inverting in color when it completes. Clicking a second time resets the countdown.

%%WGT0{"w":[{"name":"eggtimer1","type":"contraption","size":[60,20],"pos":[226,161],"def":"eggtimer","widgets":{"b":{},"s":{},"a":{}}}],"d":{"eggtimer":{"name":"eggtimer","size":[60,20],"resizable":1,"margin":[5,5,5,5],"description":"a configurable countdown timer.","script":"on get_seconds do 0+s.text end\non set_seconds x do s.text:x end\n\non view do\n b.font:card.font\n if b.animated\n  e:(sys.ms-a.text)/1000\n  b.text:r:floor 0|get_seconds[]-e\n  if r=0\n   if b.show=\"solid\" card.event[\"finish\"] end\n   b.show:\"invert\"\n  end\n else\n  b.show:\"solid\"\n  b.text:s.text\n end\nend\n\non click do\n a.text:sys.ms\n b.animated:!b.animated\n view[]\nend","template":"on finish do\n \nend","attributes":{"name":["seconds"],"label":["Seconds"],"type":["number"]},"widgets":{"b":{"type":"button","size":[60,20],"pos":[0,0],"font":"body","text":"10"},"s":{"type":"field","size":[52,18],"pos":[4,-81],"locked":1,"style":"plain","value":"10"},"a":{"type":"field","size":[53,20],"pos":[3,-57],"locked":1,"style":"plain"}}}}}
Developer (1 edit) (+4)

Behold the beauty of

The eye contraption:

A configurable pupil image follows the pointer around the card, constrained by an oval inscribed within the contraption's bounding box. In most cases, you'll want this contraption to be shown in "transparent" mode, with the rest of the eyeball drawn on the card background, but there are also interesting possibilities for placing eyes behind partially-transparent canvases. (Note: make sure you upgrade to Decker v1.13 before using this contraption!)

To specify the pupil image, copy an image using the "select" tool and paste it into the "pupil image" rich-text field in the "Eye Properties" panel as shown above. You may want to use the "View -> Transparency Mask" setting to make parts of the pupil opaque white.

%%WGT0{"w":[{"name":"eye1","type":"contraption","size":[100,100],"pos":[88,220],"show":"transparent","def":"eye","widgets":{"pupil":{},"img":{}}}],"d":{"eye":{"name":"eye","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"An animated eyeball that follows the user's pointer around the card. Best shown \"transparent\" with a blank eye underneath.","script":"on get_pupil do\n img.value\nend\n\non set_pupil x do\n img.value:x\n view[]\nend\n\non view do\n i:first extract arg where arg..type=\"image\" from img.value\n pupil.size:i.size\n pupil.clear[]\n pupil.paste[i 0,0 1]\n \n c:card.size/2\n p:pupil.size/2\n d:pointer.pos-card.offset+c\n m:(c-p)&mag d\n pupil.pos:(c-p)+m*unit heading d\nend","attributes":{"name":["pupil"],"label":["Pupil\nImage"],"type":["rich"]},"widgets":{"pupil":{"type":"canvas","size":[22,23],"pos":[110,44],"locked":1,"animated":1,"show":"transparent","border":0,"image":"%%IMG0ABYAFwAAAAH8AAf/AA//gB//wD//4H//8H//8P//+P//+P//+P//+P//+P//+P//+H//8H//8D//4B//wA//gAf/AAH8AAAAAA==","scale":1},"img":{"type":"field","size":[27,28],"pos":[-47,-56],"locked":1,"border":0,"value":{"text":["","i"],"font":["",""],"arg":["","%%IMG0ABYAFwAAAAH8AAf/AA//gB//wD//4H//8H//8P//+P//+P//+P//+P//+P//+P//+H//8H//8D//4B//wA//gAf/AAH8AAAAAA=="]}}}}}}
Developer (1 edit) (+3)

Hey kid, want a chicken? Chickens float. They all float when you're using

The "bob" Contraption:

Works much like the "eye" contraption: copy and paste a (probably transparent) image into the properties panel of the bob widget and your image will hover ominously above a shadow. Adjust the size of the contraption to control how vigorously the object bobs.

%%WGT0{"w":[{"name":"bob1","type":"contraption","size":[100,126],"pos":[305,100],"def":"bob","widgets":{"bg":{"size":[100,126],"image":"%%IMG2AGQAfgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wDpAQ8ATwEbAEYBIQBBASUAPgEnAD0BJwA+ASUAQQEhAEcBGQBRAQ0A/wD/AOk="},"spr":{"size":[51,91],"pos":[24,20]},"img":{"value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2ADMAWwDvIAQALyADAQIALSADAQEgAwAqIAYBAiABACggAwEBIAQBASACACYgBwEBIAIBASACAQEAJCADAQEgBQECIAIBASABACIgAwEBIAIBASAFAQEgAgEBIAEAIiAFAQEgAwECIAIBAiABAQEgAQAgIAEBASAFAQEgAgEBIAEBAiACAQEgAgAgIAQBAyABAQEgCAEBIAEAHyAEAQEgAwEBIAIBASABAQEgAQECIAIBAQAfIAEBASABAQIgAQECIAEBASABAQIgBAEBIAMAHiADAQIgAgEEIAIBBCAEAB0gAgECIAEBASACAQEgAwEBIAEBAyACACABASADAQEgAQECIAEBASABAQEgAQEBIAIBAgAgIAMBASACAQIgAgEDIAIBAyABAQEAHyABAQEgAgEFIAEBAyABAQEgAgECIAEAHwECIAEBAiABAQEgAgEEIAIBBCABAB8gAgEDIAEBCyABAQIAHgECIAEBBSABAQogAQEBAB4gAgEFIAEBASABAQogAQAeIAEBFAAeIAEBCCABAQYgAQEEAB0BCCABAQogAQECABsBASACAQMgAQEGIAEBCgAZAQEgAgECIAEBCiABAQMgAQEBIAIBAgAXAQEgAwEBIAEBAiABAQ0gAQEEIAEAFiAFAQEgAQETIAIBAQAVIAcBBSABARAgAQAVIAEBASABAQEgAwEBIAEBFgATIAEBASABAQIgAwEBIAEBASABAQggAQELABIgAwECIAIBAiABAQEgAgEDIAEBEQAQAQEgAgEBIAEBASACAQMgAQEKIAEBDAAPAQEgAQECIAEBASABAQIgAgEEIAEBASACAQIgAQEOAA8BASACAQIgAQEBIAEBASABAQMgAQEHIAEBByABAQYgAQAOIAEBAyABAQEgAQEEIAEBAiABAQogAQELIAEADQEBIAEBASADAQEgAgEHIAEBFCABAA0gAQEBIAEBASABAQIgAgEdIAEADAEDIAEBASABAQMgAQEBIAEBGSABAQEADAEDIAIBASABAQcgAQEWIAEBAgALAQEgAgEjIAEBAQALAQwgAQEaAAwBJwAMARkgAQEMAA0BJgANAQEgAQEkAAwBBCABASIACwEDIAEBIwAMIAEBJSABAAwBASACASQACwECIAIBIwAMAScADAECIAEBJAAMIAEBAiABASMADAEVIAEBEAANASYADQEFIAEBIAAOASUADgEUIAEBDwAQASMAEQEhABMgAQEQIAEBDQAVIAMBCyABAQ8AGQEYABsgAQEWAB0gAQETACABDiABAQMAISABAQIAAiABAQUABQECACEgAQECAA0BAwAgIAEBAgAOAQIAIQEDAA0gAQECACAgAQECAA0gAQECACAgAQECAA0gAQEDAB4BBQAMIAEBBAAcIAEBBgAKAQcAGyABAQMgAQEDAAggAQEIABoBAwACIAEBAgAHIAEBAyABAQYAGQEDAAIgAQEDAAYgAQEDAAEBAyABAQMAGAECAAQBAwAGIAEBAgACAQIAAiABAQIAGCABAAUBAgAHIAEBAgACAQIAAwECAB4BAgAIIAEAAiABAQIAAwECAB4BAgAMAQIAAwECAB8BAQALIAEBAgAkAQEACyABAQIAMCABAQEAMSACABA="]}}}}],"d":{"bob":{"name":"bob","size":[100,100],"resizable":1,"margin":[5,5,5,5],"description":"Animate an object bobbing in midair with a shadow.","script":"on get_object do img.value end\non set_object x do img.value:x view[] end\n\non oval pos size do\n flip pos+size*flip unit 2*pi*(range 20)/20\nend\n\non view do\n i:first extract arg where arg..type=\"image\" from get_object[]\n spr.size:i.size\n spr.clear[]\n spr.paste[i]\n \n bh:.9*bg.size[1]\n sh:spr.size[1]\n sy:.5*bh-sh\n t:(card.index)+sys.ms*0.002\n spr.pos:(.5*bg.size[0]-spr.size[0]),sy+sy*sin t\n\n bg.clear[]\n bg.poly[oval[bg.size*.5,.9 (.4+.2*1+sin t)*(.5*spr.size[0]),.05*bg.size[1]]]\nend","attributes":{"name":["object"],"label":["Object"],"type":["rich"]},"widgets":{"bg":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"show":"transparent","border":0,"scale":1},"spr":{"type":"canvas","size":[23,23],"pos":[38,63],"locked":1,"animated":1,"show":"transparent","border":0,"scale":1},"img":{"type":"field","size":[26,23],"pos":[0,-46],"locked":1,"value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2ABcAFwBIAREABgEBDQ8BAQAGAQENDwEBAAYBAQ0PAQEABgEBDQ8BAQAGAQENDwEBAAYBAQ0PAQEABgEBDQ8BAQAGAQENDwEBAAYBAQ0PAQEABgEBDQ8BAQAGAQENDwEBAAYBAQ0PAQEABgEBDQ8BAQAGAQENDwEBAAYBAQ0PAQEABgERAEg="]}}}}}}
Developer (1 edit) (+5)

The v1.14 release introduced a generalization for read[], allowing scripts to break an animated GIF image down into frames. There are many interesting ways to take advantage of this functionality. For example,

The "gif" Contraption:

At last, an easy way to import animated GIFs into decks! This contraption prompts the user to select a GIF file, unpacks and dithers it to 1-bit, automatically resizes itself to match the size of the image, and then loops the frames at 30fps:

Note that using GIF widgets can quickly expand the size of your decks- use them sparingly, and avoid importing huge or overly-long animations! Decker's GIF loader is brand-new and may have some quirks to hammer out, so remember to save often while playing with this feature.

%%WGT0{"w":[{"name":"gif1","type":"contraption","size":[100,100],"pos":[206,121],"def":"gif","widgets":{"c":{},"f":{},"b":{}}}],"d":{"gif":{"name":"gif","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"Import and play animated gifs. Careful: huge gifs can quickly bloat the size of your deck!","script":"on view do\n fr:extract arg where arg..type=\"image\" from f.value\n c.show:card.show\n c.clear[]\n if count fr\n  b.show:\"none\"\n  i:fr[(count fr)%sys.ms/2*60]\n  card.size:i.size\n  c.paste[i]\n else\n  b.show:\"solid\"\n end\nend","widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"border":0,"scale":1},"f":{"type":"field","size":[73,35],"pos":[8,-50],"locked":1},"b":{"type":"button","size":[69,20],"pos":[16,40],"script":"on click do\n g:\"[255,0,255,246,148,108,132,51,61,147,144,86,66,111,185,134,69,0]\"\n grays:(0,1,32+range 16) dict \"%j\" parse g\n f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i.map[grays].transform[\"dither\"]]\n end\n view[]\nend","text":"Open Gif..."}}}}}

The "scrubber" Contraption:

This contraption doesn't animate automatically; instead, it allows a user to manually scrub back and forth through the frames of the animation. It also demonstrates importing color images (resampled to the Decker 16-color palette) instead of 1-bit dithering.

%%WGT0{"w":[{"name":"scrubber1","type":"contraption","size":[100,100],"pos":[49,108],"def":"scrubber","widgets":{"c":{},"f":{},"b":{},"s":{}}}],"d":{"scrubber":{"name":"scrubber","size":[100,100],"resizable":1,"margin":[0,0,0,19],"description":"Import animated gifs and allow the user to manually scrub through the frames. Careful: huge gifs can quickly bloat the size of your deck!","script":"on change do\n view[]\nend\n\non view do\n fr:extract arg where arg..type=\"image\" from f.value\n s.interval:0,(count fr)-1\n c.show:card.show\n c.clear[]\n if count fr\n  b.show:\"none\"\n  i:fr[s.value]\n  card.size:i.size+s.size*0,1\n  c.paste[i]\n else\n  b.show:\"solid\"\n end\nend","widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"border":1,"scale":1},"f":{"type":"field","size":[73,35],"pos":[8,-50],"locked":1},"b":{"type":"button","size":[69,20],"pos":[16,40],"script":"on click do\n f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i]\n end\n view[]\nend","text":"Open Gif..."},"s":{"type":"slider","size":[100,17],"pos":[0,83],"interval":[-1,0]}}}}}

There are many variations possible on the above ideas that you might want, like looping back-and-forth, rescaling imported images to fit the bounding box of the contraption, or firing events to drive other animations. Feel free to share your own takes!

Developer(+2)

A nice, simple one this time-

The "enum" Contraption:


Works just like a "compact" slider widget, but instead of a numeric value within a range, it chooses a string from a list of newline-delimited "options". This widget respects the "font", "show", and "locked" properties, and produces a "change[]" event just like a slider.

%%WGT0{"w":[{"name":"enum1","type":"contraption","size":[100,25],"pos":[289,76],"script":"on change val do\n \nend","def":"enum","widgets":{"s":{"interval":[0,2],"format":"one"},"o":{"value":"one\ntwo\nthree"}}}],"d":{"enum":{"name":"enum","size":[100,25],"resizable":1,"margin":[5,5,5,5],"description":"select from among enumerated string values.","script":"on get_options   do o.text end\non get_value     do (\"\\n\" split o.text)[s.value] end\non set_options x do o.text:x s.value:0 view[] end\n\non set_value x do\n v:\"\\n\" split o.text\n d:v dict range count v\n s.interval:0,(count v)-1\n s.value:v[0] unless d[x]\n s.format:v[s.value]\n s.font:card.font\n s.show:card.show\n s.locked:card.locked\nend\n\non change do\n view[]\n card.event[\"change\" get_value[]]\nend\n\non view do\n v:\"\\n\" split o.text\n s.interval:0,(count v)-1\n s.format:v[s.value]\n s.font:card.font\n s.show:card.show\n s.locked:card.locked\nend\n","template":"on change val do\n \nend","attributes":{"name":["options"],"label":["Options"],"type":["code"]},"widgets":{"s":{"type":"slider","size":[100,25],"pos":[0,0],"interval":[0,100],"style":"compact"},"o":{"type":"field","size":[100,20],"pos":[0,-30],"locked":1,"style":"plain"}}}}}
Developer

A handy variation on the "enum",

The imageEnum Contraption:

Load this contraption with a selection of images, and then pick between them. Exposes attributes "value" (an Image interface, read-only!), "imageIndex" (an integer, read/write), respects the standard "show" and "locked" attributes, and provides a "change[]" event, just like enums and the slider.

%%WGT0{"w":[{"name":"imageEnum1","type":"contraption","size":[63,34],"pos":[229,154],"def":"imageEnum","widgets":{"s":{"size":[63,34]},"c":{"size":[33,30],"image":"%%IMG2ACEAHgD/AP8A/wDh"},"o":{"size":[44,31],"pos":[92,-20]}}}],"d":{"imageEnum":{"name":"imageEnum","size":[100,100],"resizable":1,"margin":[17,3,18,5],"description":"select from among enumerated image values.","script":"on images do\n extract arg where arg..type=\"image\" from o.value\nend\n\non get_options do o.value end\non set_options x do o.value:x view[] x end\non get_value do images[][s.value] end\non get_valueIndex do s.value end\non set_valueIndex x do s.value:x view[] x end\n\non view do\n im:images[]\n i:im[s.value]\n s.interval:0,0|(count im)-1\n s.locked:card.locked\n s.show  :card.show\n c.show  :card.show\n c.clear[]\n c.paste[i .5*c.size-i.size 1]\nend\n\non change do\n card.event[\"change\" get_value[] s.value]\n view[]\nend","template":"on change img index do\n \nend","attributes":{"name":["options"],"label":["Options"],"type":["rich"]},"widgets":{"s":{"type":"slider","size":[100,100],"pos":[0,0],"interval":[0,0],"format":"","style":"compact"},"c":{"type":"canvas","size":[70,96],"pos":[15,2],"locked":1,"border":0,"image":"%%IMG2AEYAYAD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AWg==","scale":1},"o":{"type":"field","size":[44,53],"pos":[129,-20],"locked":1,"show":"none"}}}}}
(+1)

is there a way to have a kind of loop, so that when you click on the right arrow when you're at the last text, it goes back to the first text, and vice versa?

Deleted 9 days ago
Developer(+1)

seeking affirmation? look no further than the

Validator Contraption:

Works much like a field, but will display informative (or annoying) text below if the value doesn't pass muster.

The validation rule(s) are provided in the script of the contraption instance, by filling out the "on check" function. This function is given the current text, and may return either an error message (a string) or the number 0 if the text is valid. In the above example, the validation function looks like so:


%%WGT0{"w":[{"name":"validator1","type":"contraption","size":[128,41],"pos":[173,242],"def":"validator","widgets":{"v":{},"e":{}}}],"d":{"validator":{"name":"validator","size":[128,41],"resizable":1,"margin":[89,5,6,22],"description":"a text input field with configurable validation rules.","script":"on is_valid  x do 0~me.event[\"check\" x] end\non get_valid   do is_valid[v.text] end\non get_text    do if is_valid[v.text] v.text else 0 end end\non set_text  x do if is_valid[x] v.text:x end end\n\non change x silent do\n err:card.event[\"check\" v.text]\n e.text:if 0~err\n  if !silent card.event[\"change\" x] end\n  \"\"\n else\n  err\n end\nend\n\non view do\n v.font  :card.font\n v.locked:card.locked\n v.show  :card.show\n e.show  :card.show\n change[v.text 1]\nend","template":"on change x do\n \nend\n\non check x do\n \nend","attributes":{"name":["text"],"label":["Text"],"type":["string"]},"widgets":{"v":{"type":"field","size":[120,17],"pos":[4,4]},"e":{"type":"field","size":[120,14],"pos":[4,23],"locked":1,"border":0,"style":"plain"}}}}}
Developer (1 edit) (+1)

Time zones are a drag. Banish them with

The .Beat Contraption:


A nice simple one. Display the current Swatch Internet Time.

%%WGT0{"w":[{"name":".beat1","type":"contraption","size":[96,28],"def":".beat","widgets":{"t":{"value":"@ 668.75"}}}],"d":{".beat":{"name":".beat","size":[96,28],"margin":[0,0,0,0],"description":"display the .beat time, also known as Swatch Internet Time.","image":"%%IMG2AGAAHAACAVwAAwECIFoBAgABAQIgAQFaIAEBAyABAVwgAQECIAEBXCABAQIgAQFcIAEBAiABAVwgAQECIAEBBiACAVQgAQECIAEBBiACAVQgAQECIAEBBiACAVQgAQECIAEBBiACARQgAgE+IAEBAiABAQYgAgEUIAIBPiABAQIgAQEGIAUBAyAEAQMgAwEBIAEBASAEAT0gAQECIAEBBiAGAQEgBgEBIAYBASAEAT0gAQECIAEBBiACAQIgAgEBIAIBAiACAQEgAgECIAIBAiACAT4gAQECIAEBBiACAQIgAgEBIAIBAiACAQEgAgECIAIBAiACAT4gAQECIAEBBiACAQIgAgEBIAYBASACAQIgAgECIAIBPiABAQIgAQEGIAIBAiACAQEgAgEFIAIBAiACAQIgAgE+IAEBAiABAQYgBgEBIAYBASAGAQIgAgE+IAEBAiABAQQgAQEBIAUBAyAEAQMgAwEBIAEBAyACAT0gAQECIAEBXCABAQIgAQFcIAEBAiABAVwgAQECIAEBXCABAQIgAQFcIAEBAyABAVogAQECAAEBAiBaAQIAAwFcAAI=","widgets":{"t":{"type":"field","size":[54,15],"pos":[39,7],"locked":1,"animated":1,"script":"on view do\n p:\"%p\" parse \"%e\" format sys.now\n b:(1/86.4)*(3600*24%p.hour+1)+(60*p.minute)+p.second\n me.text:\"@ %4.2f\" format b\nend","font":"mono","show":"invert","border":0,"style":"plain"}}}}}

No configuration options for this one. It might be a fun exercise to try creating an enhanced version that fires an event when the .beat advances!

Developer (2 edits) (+1)

Tired of inert masses? Why not bring them to life with

The Pulse Contraption

Turn your favorite image into a pulsating mass! Works much like the Eye Contraption; copy and paste an image of your choice into an instance of the Pulse Contraption and it will begin to writhe and stretch, scaling from its initial size to the bounds of the contraption. It is also possible to adjust the speed and whether the axes scale in sync, or offset with one another.

%%WGT0{"w":[{"name":"Pulse1","type":"contraption","size":[100,100],"pos":[49,189],"def":"Pulse","widgets":{"c":{},"o":{},"i":{},"s":{}}}],"d":{"Pulse":{"name":"Pulse","size":[100,100],"resizable":1,"margin":[5,5,5,5],"description":"A quivering, pulsating mass.","script":"on get_sprite   do i.value   end\non set_sprite x do i.value:x end\non get_axes     do o.value   end\non set_axes   x do o.value:x end\non get_speed    do s.text+0  end\non set_speed  x do s.text:x  end\n\non view do\n img:first extract arg where arg..type=\"image\" from i.value\n scl:.5*1+sin(o.value*pi*0,1)+(.02*s.text)*sys.frame\n size:img.size+scl*c.size-img.size\n pos:.5*c.size-size\n c.clear[]\n c.paste[img pos,size 1]\nend","attributes":{"name":["sprite","axes","speed"],"label":["Image","Offset Axes?","Speed"],"type":["rich","bool","number"]},"widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"show":"transparent","border":0,"scale":1},"o":{"type":"button","size":[99,20],"pos":[131,-30],"locked":1,"show":"none","text":"Offset Axes?","style":"check","value":0},"i":{"type":"field","size":[100,20],"pos":[130,-1],"locked":1,"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2ABIAEgAGAQYACgEKAAcBDAAFAQUNBAEFAAMBBA0IAQQAAgEDDQoBAwABAQQNCgEHDQwBBg0MAQYNDAEGDQwBBw0KAQQAAQEDDQoBAwACAQQNCAEEAAMBBQ0EAQUABQEMAAcBCgAKAQYABg=="]}},"s":{"type":"field","size":[100,20],"pos":[130,28],"locked":1,"show":"none","style":"plain","value":"1"}}}}}

Note: large pulsating images may cause Web-Decker to bog down on slower machines, and line drawings generally produce better-looking results than dithered images.

Developer (1 edit)

Make your deck teem with activity using

The Turtle Contraption:


Just like its namesake animal, the Turtle Contraption is a little triangular critter that moves around. If you drag them around manually you can reposition them on the card.

The behavior of each turtle is controlled by their script. The "move" function is given a dictionary containing the turtle's "pos" (position) and "dir" (direction). This function should return a modified dictionary containing a new position and direction, and can optionally set "pat" (a pattern number for drawing the turtle in) and "fd" (a number of pixels to move forward in the current direction). From these building blocks, many interesting behaviors can be built, as demonstrated above:


The definition below is pre-loaded with the last of the above behaviors, which is perhaps the most interesting: it wanders around and avoids any "walls" that have been drawn on the card background. These turtles aren't extremely good at solving mazes, but perhaps you could improve their performance?

%%WGT0{"w":[{"name":"flatlander","type":"contraption","size":[30,30],"pos":[219,59],"script":"on move x do\n d:0 while (d<30)&!card.image[floor x.pos+d*unit x.dir] d:d+1 end\n x.dir:x.dir+if d<20 .01 else .001 end*random[100]-50\n x.fd :d>10\n x.pat:8+d<10\nend","show":"transparent","def":"turtle","widgets":{"c":{},"s":{}}}],"d":{"turtle":{"name":"turtle","size":[30,30],"margin":[0,0,0,0],"description":"a programmable embodied agent.","script":"on get_state   do s.text   end\non set_state x do s.text:x end\n\non view do\n # state management\n os:\"%j\" parse s.text\n offset:card.size/2\n center:card.pos+offset\n is.pos :os.fp+center\n is.dir :os.dir\n is:card.event[\"move\" is]\n c.pattern:os.pat:32 unless is.pat\n if \"fd\" in is is.pos:is.pos+is.fd*unit is.dir end\n os.dir :is.dir\n os.fp  :is.pos-floor is.pos\n card.pos:(512,342)%is.pos-offset\n s.text:\"%j\" format os\n \n # rendering\n tri:4 take flip offset+flip(14,10,10)*unit os.dir+(.666*pi)*range 3\n c.clear[]\n c.poly[tri]\n c.pattern:1\n c.line[tri]\nend","template":"on move x do\n x\nend","attributes":{"name":["state"],"label":["State"],"type":["code"]},"widgets":{"c":{"type":"canvas","size":[30,30],"pos":[0,0],"locked":1,"animated":1,"script":"on drag do\n card.pos:pointer.pos-me.size/2 \n me.pos:0\nend","show":"transparent","border":0,"draggable":1,"scale":1},"s":{"type":"field","size":[100,20],"pos":[57,-4],"show":"none"}}}}}
(4 edits) (+2)

JankyTunes: The Contraption


Do you want to play back cute little chiptune songs in your Decker decks? Building on my Janky Sequencer project, I've refactored that code into a contraption that you can use to play tunes you write in it inside your own decks!

You can grab the code for the contraption from inside the deck on the project page along with the instructions and some example stuff: https://micpp.itch.io/jankytunes-the-contraption

Or grab it from below!

%%WGT0{"w":[{"name":"jankytunes1","type":"contraption","size":[193,72],"pos":[159,135],"def":"jankytunes","widgets":{"notes":{},"pitches":{},"synthparams":{},"button1":{},"playbegin":{},"playstop":{},"loop":{},"playing":{},"lastframe":{},"buffer1":{},"buffer2":{},"buffer3":{},"buffer4":{}}}],"d":{"jankytunes":{"name":"jankytunes","size":[193,72],"margin":[0,0,0,0],"description":"a player for jankysequencer format music","script":"on view do\n if count notes.value\n  button1.show:\"none\"\n  button1.locked:1\n  playbegin.show:\"solid\"\n  playbegin.locked:0\n  playstop.show:\"solid\"\n  playstop.locked:0\n  loop.show:\"solid\"\n  loop.locked:0\n else\n  button1.show:\"solid\"\n  button1.locked:0\n end\nend\n\non get_row do\n notes.row\nend\n\non set_row x do\n notes.row:x\nend\n\non get_loop do\n loop.value\nend\n\non set_loop x do\n loop.value:x\nend\n\non get_playfromstart do\n playfromstart\nend\n\non get_play do\n startplayback\nend\n\non get_stop do\n stopplayback\nend\n\non get_isplaying do\n playing.value\nend\n\non playfromstart do\n notes.row:0\n startplayback[]\nend\n\non startplayback do\n playstop.text:\"Stop\"\n #calculate row into buffer\n calcrow[]\n lastframe.value:sys.frame\n playbuffer[]\n #calc next row\n calcnextrow[]\n playing.value:1\nend\n\non stopplayback do\n playing.value:0\n playstop.text:\"Play\"\n card.event[\"playstopped\"]\nend\n\non calcnextrow do\n if notes.row = ((count notes.value)-1)\n  notes.row:0\n  calcrow[]\n  notes.row:((count notes.value)-1)\n else\n  notes.row:notes.row+1\n  calcrow[]\n  notes.row:notes.row-1\n end\nend\n\non calcrow do\n #voice1\n if !(notes.rowvalue[\"Voice1\"]=\"\")\n  buffer1.value:calcnote[notes.rowvalue[\"Voice1\"]\n   notes.rowvalue[\"Len1\"] 1].encoded\n else buffer1.value:\"\"\n end\n #voice2\n if !(notes.rowvalue[\"Voice2\"]=\"\")\n  buffer2.value:calcnote[notes.rowvalue[\"Voice2\"]\n   notes.rowvalue[\"Len2\"] 2].encoded\n else buffer2.value:\"\"\n end\n #voice3\n if !(notes.rowvalue[\"Voice3\"]=\"\")\n  buffer3.value:calcnote[notes.rowvalue[\"Voice3\"]\n   notes.rowvalue[\"Len3\"] 3].encoded\n else buffer3.value:\"\"\n end\n #noise\n if !(notes.rowvalue[\"Noise\"]=\"\")\n  buffer4.value:calcnoise[notes.rowvalue[\"Noise\"]].encoded\n else buffer4.value:\"\"\n end\nend\n\non playbuffer do\n play[sound[buffer1.value[0].text]]\n play[sound[buffer2.value[0].text]]\n play[sound[buffer3.value[0].text]]\n play[sound[buffer4.value[0].text]]\nend\n\non calcnoise beat do\n #calculate length\n tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n length:(tempo/60)*8000*beat\n \n  v:(first extract value where param=\"volume4\" from synthparams.value)+0\n  a:(first extract value where param=\"attack4\" from synthparams.value)+0\n  d:(first extract value where param=\"decay4\" from synthparams.value)+0\n  s:(first extract value where param=\"sustain4\" from synthparams.value)+0\n  r:(first extract value where param=\"release4\" from synthparams.value)+0\n  \n  l:(length-a)-d\n  if l<0 l:0 end\n  x:range (length+r)\n  \n  noise:random[((range 2*v)-v) length+r]\n  \n  sound[envelope[x a d s/100 r l 1]*noise]\nend\n\non calcnote note beat voice do\n #calculate pitch\n if (count note) = 3\n  octave: note[2]\n  basenote: \"\" fuse (note[0], note[1])\n else\n  octave: note[1]\n  basenote: note[0]\n end\n basepitch: (first extract pitch where note=basenote from pitches.value)+0\n pitch:basepitch*2^(octave-4)\n \n #calculate length\n tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n length:(tempo/60)*8000*beat\n \n #generate waveform + envelope\n \n if voice=1\n  wavetype:first extract value where param=\"wavetype1\" from synthparams.value\n  v:(first extract value where param=\"volume1\" from synthparams.value)+0\n  a:(first extract value where param=\"attack1\" from synthparams.value)+0\n  d:(first extract value where param=\"decay1\" from synthparams.value)+0\n  s:(first extract value where param=\"sustain1\" from synthparams.value)+0\n  r:(first extract value where param=\"release1\" from synthparams.value)+0\n elseif voice=2\n  wavetype:first extract value where param=\"wavetype2\" from synthparams.value\n  v:(first extract value where param=\"volume2\" from synthparams.value)+0\n  a:(first extract value where param=\"attack2\" from synthparams.value)+0\n  d:(first extract value where param=\"decay2\" from synthparams.value)+0\n  s:(first extract value where param=\"sustain2\" from synthparams.value)+0\n  r:(first extract value where param=\"release2\" from synthparams.value)+0\n else\n  wavetype:first extract value where param=\"wavetype3\" from synthparams.value\n  v:(first extract value where param=\"volume3\" from synthparams.value)+0\n  a:(first extract value where param=\"attack3\" from synthparams.value)+0\n  d:(first extract value where param=\"decay3\" from synthparams.value)+0\n  s:(first extract value where param=\"sustain3\" from synthparams.value)+0\n  r:(first extract value where param=\"release3\" from synthparams.value)+0\n end\n \n l:(length-a)-d\n if l<0 l:0 end\n \n if wavetype=\"square\"\n  wave:squarewave\n elseif wavetype=\"sawtooth\"\n  wave:sawwave\n else\n  wave:sinewave\n end\n \n x:range (length+r)\n \n sound[envelope[x a d s/100 r l v]*wave[pitch x]]\n\nend\n\non sinewave pitch x do\n sin (pitch/8000)*2*pi*x\nend\n\non squarewave pitch x do\n sine:sinewave[pitch x]\n (sine>0) - (sine<0) - (sine=0)\nend\n\non sawwave pitch x do\n l:8000/pitch\n floatmod:((l*10000)%(x*10000))/10000\n (((floatmod) / l)*2)-1\nend\n\non envelope x a d s r l v do\n ((x<a) *="" ((v="" a)*x))="" +="" #attack\n="" ((x="a)*v)" +\n="" (((a<x)="" &="" (x<(a+d)))="" ((((v*(s-1))="" d)*x)="" v="" -(((v*(s-1))="" d)*a)))="" #decay\n="" (s*v))="" ((((a+d)<x)="" (x<(a+d+l)))="" #sustain\n="" ((((a+d+l)<x)="" (x<(a+d+l+r)))="" ((((-s*v)="" r)*x)="" ((s*v)="" r)*(a+d+l+r)))="" #release\nend","template":"on="" playstopped="" do\n\nend\n\non="" reachedend="" do\n\nend","attributes":{"name":["loop","row"],"label":["looping?","current="" row"],"type":["bool","number"]},"widgets":{"notes":{"type":"grid","size":[100,50],"pos":[226,-62],"locked":1,"value":{},"row":20},"pitches":{"type":"grid","size":[100,50],"pos":[237,38],"locked":1,"value":{}},"synthparams":{"type":"grid","size":[100,50],"pos":[237,132],"locked":1,"value":{}},"button1":{"type":"button","size":[72,20],"pos":[59,28],"script":"on="" click="" do\n="" alert[\"select="" the="" notes="" csv\"]\n="" notes.value:readcsv[read[\"text\"]]\n="" synth="" parameters="" synthparams.value:readcsv[read[\"text\"]]\n="" pitches="" pitches.value:readcsv[read[\"text\"]]\n="" me.show:\"none\"\n="" me.locked:1\n="" notes.row:0\n="" view[]\nend","text":"load="" song"},"playbegin":{"type":"button","size":[102,20],"pos":[10,7],"locked":1,"script":"on="" playfromstart[]\nend","show":"none","text":"play="" from="" start"},"playstop":{"type":"button","size":[60,20],"pos":[120,7],"locked":1,"script":"on="" if="" !playing.value\n="" startplayback[]\n="" else\n="" stopplayback[]\n="" end\nend","show":"none","text":"play"},"loop":{"type":"button","size":[60,20],"pos":[66,44],"locked":1,"show":"none","text":"loop","style":"check","value":0},"playing":{"type":"button","size":[68,20],"pos":[115,147],"locked":1,"text":"playing","style":"check","value":0},"lastframe":{"type":"field","size":[100,20],"pos":[-42,141],"locked":1,"animated":1,"script":"on="" change="" val="" \nend\n\non="" view="" playing.value\n="" tempo:(first="" extract="" value="" where="" param="\"tempo\"" synthparams.value)+0\n="" !((lastframe.value[0].text+tempo)="">sys.frame)\n   lastframe.value:sys.frame  \n   #play the current row from buffer\n   if notes.row = ((count notes.value)-1)\n    if loop.value\n     notes.row:0\n     notes.scroll:0\n     playbuffer[]\n     calcnextrow[]\n    else\n     stopplayback[]\n     card.event[\"reachedend\"]\n    end\n   else\n    notes.row:notes.row+1\n    notes.scroll:notes.row\n    playbuffer[]\n    calcnextrow[]\n   end\n  end\n end\nend","value":"0"},"buffer1":{"type":"field","size":[100,20],"pos":[-139,-105],"locked":1},"buffer2":{"type":"field","size":[100,20],"pos":[-15,-105],"locked":1},"buffer3":{"type":"field","size":[100,20],"pos":[110,-106],"locked":1},"buffer4":{"type":"field","size":[100,20],"pos":[232,-104],"locked":1}}}}}</a)>

If you use this in a deck, I'd appreciate if you gave me credit - also please let me know if you do as I'd love to see what you can come up with!

(+3)

wow! I came here today to say how impressed I was by your July jam musical decks. This contraption is next-level!

(+1)

Updated this contraption to work with the changes in Decker 1.32

Developer (1 edit) (+1)

The following contraptions demonstrate Decker 1.25: contraptions can now be tools to help manipulate and organize the contents of decks. Be sure to upgrade before giving these a spin!

The decaying web sure is a bummer. Turn your gaze inward with

The SearchEngine Contraption:

Type a short phrase into the searchEngine textbox and it will scour your deck for any cards or widgets containing it (case-insensitive), producing a list of clickable links to the cards it finds.

  • If any card contains a widget named "noindex", it will be skipped.
  • The searchEngine considers the .text attribute of every widget. If you're using contraptions with content you want searched, expose it via a custom attribute!
  • The searchEngine also considers invisible widgets; you can add hidden fields to supplement the visible content of cards.
%%WGT0{"w":[{"name":"deckDeckGo","type":"contraption","size":[209,81],"pos":[149,239],"def":"searchEngine","widgets":{"i":{},"o":{"show":"none"}}}],"d":{"searchEngine":{"name":"searchEngine","size":[209,81],"resizable":1,"margin":[5,29,21,6],"description":"search the contents of your deck and easily navigate to cards with your search keyword(s).","script":"on change do\n s:\"%l\" format i.text\n r:0 take rtext.make[]\n if count s\n  each c k in deck.cards\n   if !\"noindex\" in c.widgets\n    f:s in \"%l\" format k\n    each w in c.widgets\n     f:f|s in \"%l\" format w.text\n    end\n    if f\n     r:r,rtext.make[k \"\" k],rtext.make[\"\\n\"]\n    end\n   end\n  end\n end\n if count r\n  o.show:\"solid\"\n  o.value:r\n else\n  o.show:\"none\"\n end\nend\n\non view do\n change[]\nend","widgets":{"i":{"type":"field","size":[203,20],"pos":[3,3],"style":"plain"},"o":{"type":"field","size":[203,51],"pos":[3,27],"locked":1,"scrollbar":1}}}}}

Want a more compact alternative to the pattern editor tool? Why not try

The PatEdit Contraption:

Scroll through any of Decker's 1-bit patterns, make changes, and watch the results live. When you're satisfied with the changes, you can even delete this contraption entirely!

%%WGT0{"w":[{"name":"patEdit","type":"contraption","size":[68,86],"pos":[29,38],"def":"patEdit","widgets":{"c":{},"p":{}}}],"d":{"patEdit":{"name":"patEdit","size":[68,86],"margin":[0,0,0,0],"description":"a mini pattern editor tool.","script":"\non edit do\n patterns[p.value]:c.copy[]\nend\n\non click pos do\n me.pattern:!me[pos]\n edit[]\nend\n\non drag pos do\n send drag[pos]\n edit[]\nend\n\non release pos do\n drag[pos]\nend\n\non change do\n c.paste[patterns[p.value]]\nend\n\non view do\n change[]\nend","image":"%%IMG2AEQAVgACAUAAAwEBIEABAQABAQEgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQIgQgECIEIBAiBCAQEAAQEBIEABAQADAQEgPgEBAAUBPgAD","widgets":{"c":{"type":"canvas","size":[64,64],"pos":[2,2],"scale":8},"p":{"type":"slider","size":[64,17],"pos":[2,67],"interval":[2,27],"value":2,"format":"%i","style":"compact"}}}}}

Tired of the same old 16-color Macintosh palette? Taste the rainbow with

The PalImport Contraption:

Pick any 16-color (or fewer) palette from LoSpec, save the "HEX" format, and use the "Import" button on this contraption to read in the file or paste it directly into the field. Then click "Apply" to replace Decker's default palette. If the input palette has 14 or fewer colors, they will be sequentially loaded into color slots 33-46 and leave pure white (32) and pure black (47) intact. Otherwise, 32 and 47 will be prioritized with the lightest and darkest color available in the palette, respectively. Just like PatEdit, when you're satisfied with the results you can delete this contraption.

%%WGT0{"w":[{"name":"palImport1","type":"contraption","size":[137,100],"pos":[334,171],"def":"palImport","widgets":{"button1":{},"button2":{},"p":{"value":"2e222f\n45293f\n7a3045\n993d41\ncd683d\nfbb954\nf2ec8b\nb0a987\n997f73\n665964\n443846\n576069\n788a87\na9b2a2\n"}}}],"d":{"palImport":{"name":"palImport","size":[137,100],"margin":[0,0,0,0],"description":"a tool for importing color palettes in the .hex format as used by lospec.com.","image":"%%IMG2AIkAZAACAYUAAwEBDYUBAQABAQENhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQEAAQEBDYUBAQADAYUAAg==","widgets":{"button1":{"type":"button","size":[60,20],"pos":[6,74],"script":"on click do\n p.text:read[]\nend","text":"Import"},"button2":{"type":"button","size":[60,20],"pos":[71,74],"script":"on color_dist a b do\n aa:\"%2h%2h%2h\" parse \"%06h\" format a\n bb:\"%2h%2h%2h\" parse \"%06h\" format b\n sum(aa-bb)^2\nend\n\non find_closest i n do\n g:patterns[i]\n r:first n\n each v in n\n  if color_dist[r g]>color_dist[v g] r:v end\n end\n patterns[i]:r\n (list r) drop n\nend\n\non click do\n n:16 limit (list \"%h\") parse \"\\n\" split p.text\n patterns[32]:16777215\n patterns[47]:0\n if (count n)>15\n  n:find_closest[32 n]\n  n:find_closest[47 n]\n end\n each c i in n patterns[33+i]:c end\nend","text":"Apply"},"p":{"type":"field","size":[125,62],"pos":[6,7],"scrollbar":1,"style":"code"}}}}}
(1 edit)

Nice! This helps a lot as you can do like duotones, tritones, sepiatone sets and such.
This is a deck global color set, correct? A card, or per canvas widget property would even more helpful.

Is there a way to hide my widget/contraption invisible as a group? That way the can appear on a card only when called for? 

Also this Decker objects format, that starts some Decker clipboard data such as "%%WDG ..." or whatever, it looks like base64 encoded binary data like a Data URL?  I want to be able to decode these into human viewable formats and then reencoded them when done.

"%%IMG2AIkAZAACAYUAAwEBDYUBAQABAQENhwE ...

Thanks!

Developer

There is one pattern set/palette per deck, but as demonstrated by the above materials it can be manipulated on the fly; it's a matter of scripting to give cards their own palette. Some of the submissions to the most recent Decker game jam, including (Don't) Save Me and I Lost My Duck take advantage of this.

There is no grouping mechanism for widgets upon a card other than Contraptions.

Decker's image encodings are described in The Decker File Format. It's also possible to use Lilt to programmatically manipulate decks, encode/decode various resource types and import/export a handful of conventional file types.

Developer(+2)

Another palette-related editor,

The AnimEdit Contraption:


This contraption provides an editor for the four animated pattern "slots". Clicking "Read" will load the sequences of patterns in each slot into the text field, and clicking "Write" will apply the contents of the text field to the deck. The slider and canvas above provide a quick reference for all the currently defined colors and patterns, by index.

Each animated pattern (slots 28, 29, 30, and 31) consists of up to 8 indices into other patterns, shown comma-separated, one pattern per line.

%%WGT0{"w":[{"name":"animEdit1","type":"contraption","size":[155,99],"pos":[248,51],"def":"animEdit","widgets":{"v":{},"r":{},"w":{},"p":{},"c":{}}}],"d":{"animEdit":{"name":"animEdit","size":[155,99],"margin":[0,0,0,0],"description":"an editor for the animated pattern sequences.","image":"%%IMG2AJsAYwADAZYABAEBDZYBAQACAQENmAECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQECDZkBAg2ZAQINmQEBAAEBAQ2XAQEAAwEBDZUBAQAFAZUAAw==","widgets":{"v":{"type":"field","size":[149,49],"pos":[3,25],"style":"code"},"r":{"type":"button","size":[72,20],"pos":[3,76],"script":"on click do\n v.text:\"\\n\" fuse each v in 28,29,30,31\n  \",\" fuse patterns[v]\n end\nend","text":"Read"},"w":{"type":"button","size":[74,20],"pos":[78,76],"script":"on click do\n each l i in 4 limit \"\\n\" split v.text\n  patterns[28+i]:0+\",\" split l\n end\nend","text":"Write"},"p":{"type":"slider","size":[72,20],"pos":[3,3],"script":"on change val do\n c.clear[]\n c.pattern:val\n c.fill[]\nend","interval":[0,47],"style":"compact"},"c":{"type":"canvas","size":[74,20],"pos":[78,3],"scale":1}}}}}
Developer (3 edits) (+2)

You spin me right round baby, like

The Rotor Contraption:


A rotor continuously rotates an image at a configurable speed. Rotors support transparency and can optionally be made draggable, like a canvas.

When pasting your image in, be sure to leave a sufficient margin: rotors spin around the centerpoint of the image, and only the circular part at the center of a rectangular image will spin "cleanly":

%%WGT0{"w":[{"name":"rotor1","type":"contraption","size":[100,100],"pos":[152,53],"show":"transparent","def":"rotor","widgets":{"c":{},"s":{},"i":{}}}],"d":{"rotor":{"name":"rotor","size":[100,100],"resizable":1,"margin":[5,5,5,5],"description":"Animate an image by making it spin!","script":"on get_speed   do 0+s.text  end\non set_speed x do s.text:x  end\non get_img   do i.value   end\non set_img x do i.value:x end\non get_draggable   do c.draggable   end\non set_draggable x do c.draggable:x end\n\non view do\n img:first extract arg where arg..type=\"image\" from get_img[]\n rot:(sys.frame/60)*2*pi*get_speed[]\n c.clear[]\n c.paste[img.copy[].rotate[rot] (c.size/2)-(img.size/2) 1]\nend","attributes":{"name":["speed","img","draggable"],"label":["Rotation Speed","Image","Draggable"],"type":["number","rich","bool"]},"widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"script":"on drag do\n if me.draggable\n   card.pos:pointer.pos-me.size/2 \n   me.pos:0\n end\nend","show":"transparent","border":0,"scale":1},"s":{"type":"field","size":[23,20],"pos":[117,2],"show":"none","style":"plain","value":"0.5"},"i":{"type":"field","size":[23,20],"pos":[117,33],"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2AEUAQwD/AP8A/wD/AHUBAQBDAQMAQgEEAEEBBABBAQUAPwEGAD8BBgA+AQcAPgEDDQEBAwA+AQMNAQEDAD4BAw0CAQMAPQEDDQIBAwA8AQMNAwEDADwBAw0DAQMAPAEDDQQBAwA6AQQNBAEDADoBAw0GAQMAOQEDDQYBAwA4AQMNBwEDADgBAw0IAQMANwEDDQkBAwA2AQMNCgEDADQBAw0MAQMAMgEDDQ0BAwAyAQMNDQEEADEBAw0OAQMAMQEDDQ4BAwAwAQMNDwEDADABAw0QAQMALwEWADABFQAvARUAMQEDAP8A/wD/AP8A/wAI"]}}}}}}

The rotor is also available in a variant,

The SeekRotor Contraption:

A seekRotor rotates to point toward something. By default, that "something" is the user's pointer. If you provide a "target[]" event handler on the contraption instance you can tell it to point toward any position you like. For example, pointing at the center of a field:


This behavior pairs nicely with making the source or target draggable, as seen above! Images for the seekRotor should face to the right in their initial view.

%%WGT0{"w":[{"name":"seekRotor1","type":"contraption","size":[100,100],"pos":[351,79],"show":"transparent","def":"seekRotor","widgets":{"c":{},"i":{}}}],"d":{"seekRotor":{"name":"seekRotor","size":[100,100],"resizable":1,"margin":[5,5,5,5],"description":"Animate an image by making turn toward the cursor.","script":"on get_img   do i.value   end\non set_img x do i.value:x end\non get_draggable   do c.draggable   end\non set_draggable x do c.draggable:x end\n\non view do\n img:first extract arg where arg..type=\"image\" from get_img[]\n trg:pointer.pos unless card.event[\"target\"]\n rot:heading trg-card.offset+card.size/2\n c.clear[]\n c.paste[img.copy[].rotate[rot] (c.size/2)-(img.size/2) 1]\nend","template":"on target do\n pointer.pos\nend","attributes":{"name":["img","draggable"],"label":["Image","Draggable"],"type":["rich","bool"]},"widgets":{"c":{"type":"canvas","size":[100,100],"pos":[0,0],"locked":1,"animated":1,"script":"on drag do\n if me.draggable\n   card.pos:pointer.pos-me.size/2 \n   me.pos:0\n end\nend\n","show":"transparent","border":0,"scale":1},"i":{"type":"field","size":[23,20],"pos":[117,33],"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2AEUAQgD/AP8A/wD/AP8AfQEBAEMBBABBAQkAPAELADoBAwkBAQwANQEDCQYBCwAxAQMJCAEMAC4BAwkNAQkALAEDCREBCAApAQMJFAEJACUBAwkWAQgAJAEDCRkBBQAkAQMJFgEHACUBAwkTAQkAJgEDCQ8BCgApAQMJBgEQACwBAwkEAQ8ALwEDCQIBDQAzAQoAPAEGAD4BBgBAAQMA/wD/AP8A/wD/AP8ArA=="]}}}}}} EDCRkBBQAkAQMJFgEHACUBAwkTAQkAJgEDCQ8BCgApAQMJBgEQACwBAwkEAQ8ALwEDCQIBDQAzAQoAPAEGAD4BBgBAAQMA/wD/AP8A/wD/AP8ArA=="]}}}}}}
Developer(+2)

bored of base 10? how about

The BitField Contraption:

The bitField contraption (shown here linked to an ordinary field widget), stores a numeric value and allows it to be viewed and edited as binary digits. Clicking toggles a bit, and dragging can set multiple bits quickly. The "width" (in bits) can be configured to any value between 1 and 32. Like a Field widget, it fires an "on change" event, and it respects the "show", "font", and "locked" common attributes.

%%WGT0{"w":[{"name":"bitField","type":"contraption","size":[216,31],"pos":[176,159],"def":"bitField","widgets":{"c":{"size":[216,31],"pattern":0},"v":{"size":[65,31],"pos":[234,0],"value":"0"},"w":{"pos":[234,45]},"d":{"pos":[234,79]}}}],"d":{"bitField":{"name":"bitField","size":[101,20],"resizable":1,"margin":[5,5,5,5],"description":"a visual, interactive display of an integer as its bits, shown from most to least significant.","script":"on get_value   do 0+v.text end\non get_width   do 0+w.text end\non set_value x do v.text:((2^w.text)-1)&0|x view[] end\non set_width x do w.text:32&1|x             view[] end\n\nbw:get_width[]\nbv:get_value[]\n\non unpack do\n t:bv each v in range bw\n  r:2%t t:floor t/2 r\n end\nend\n\non view do\n c.clear[]\n c.font:card.font\n c.show:card.show\n cell:card.size/bw,1\n t:bv\n each bit i in unpack[]\n  r:cell*((bw-1)-i),0\n  c.pattern:1\n  if bit c.rect[r cell+1] end\n  c.pattern:!bit\n  c.text[bit r+cell/2 \"center\"]\n end\nend\n\non drag pos do\n bit:(bw-1)-floor pos[0]/card.size[0]/bw\n if !card.locked\n  set_value[if d.value\n   bits.and[bv bits.xor[(2^bw)-1 2^bit]]\n  else\n   bits.or[bv 2^bit]\n  end]\n  card.event[\"change\" get_value[]]\n end\nend\n\non click pos do\n bit:(bw-1)-floor pos[0]/card.size[0]/bw\n d.value:bits.and[bv 2^bit]\n drag[pos]\nend\n\non release pos do\n drag[pos]\nend","template":"on change val do\n \nend","attributes":{"name":["value","width"],"label":["Value","Width"],"type":["number","number"]},"widgets":{"c":{"type":"canvas","size":[101,20],"pos":[0,0],"locked":1,"border":1,"scale":1},"v":{"type":"field","size":[65,20],"pos":[119,0],"show":"none","style":"plain","value":"0"},"w":{"type":"field","size":[65,20],"pos":[119,34],"show":"none","style":"plain","value":"8"},"d":{"type":"button","size":[60,20],"pos":[119,68],"show":"none","style":"check","value":0}}}}}
Developer (1 edit) (+2)

Get the inside story with

The Interior Contraption:


The Interior contraption is a draggable object which reveals part of the background image from another card based on a "mask". Any part of the mask image that contains cyan pixels will be replaced as the contraption is moved around. If you specify the name of more than one card (in a comma-separated list), they will be cycled as an animation. A quick demo of setting this contraption up:


%%WGT0{"w":[{"name":"interior1","type":"contraption","size":[94,97],"pos":[282,140],"show":"transparent","def":"interior","widgets":{"c":{"size":[94,97]},"i":{"size":[100,28],"pos":[149,-5]},"n":{"size":[100,31],"pos":[149,46]}}}],"d":{"interior":{"name":"interior","size":[63,63],"resizable":1,"margin":[5,5,5,5],"description":"display an \"interior\" within the cyan pixels of a mask image composited in from another card.","script":"on get_mask do i.value end\non get_card do n.text  end\non set_mask x do i.value:x end\non set_card x do n.text:x  end\n\non view do\n mask:first extract arg where arg..type=\"image\" from i.value\n card.size:c.size:mask.size\n cn:\",\" split n.text\n cb:deck.cards[cn[(count cn)%floor sys.frame/10]].image\n if cb.type~\"image\"\n  cb:cb.copy[card.pos card.size].map[0 dict 32]\n  c.clear[]\n  c.paste[mask.copy[].map[(colors.cyan dict 1) 0]]\n  c.merge[mask.copy[] cb]\n end\nend\n\non drag do\n card.pos:pointer.pos-c.size/2\nend","attributes":{"name":["mask","card"],"label":["Mask","Card(s)"],"type":["rich","string"]},"widgets":{"c":{"type":"canvas","size":[63,63],"pos":[0,0],"locked":1,"animated":1,"show":"transparent","border":0,"draggable":1,"scale":1},"i":{"type":"field","size":[100,20],"pos":[118,-5],"locked":1,"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2AD8APwFAJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BAic9AQInPQECJz0BQA=="]}},"n":{"type":"field","size":[100,20],"pos":[118,30],"locked":1,"show":"none","style":"plain"}}}}}
(+1)

man this is SO cool !!!!!!!

Developer(+2)

There are a lot of interesting ways this contraption- or similar compositing techniques- could be used. I'd love to see what you can come up with!

(2 edits) (+1)

I took on the (admittedly simple) challenge of emitting an event when the .beat internet time changes. I only discovered Decker today, so suggestions are welcome if I've misunderstood concepts or am using things wrong (i'm pretty sure preserving state between `view` events is not supposed to be done via an off-canvas textbox, but i wanted to avoid emitting an event more than once for the same timestamp), but here's an event-emitting .beat time box:


I've updated it to include a template for the beat_tick event, and  optimised things a little  by not doing any string formatting at all if the time hasn't changed  (reducing how much stuff happens at 60Hz), eliminating the parse call for sys.now (thanks for answering my modulo question over on cohost!)  and inlining the calculation in the format call. This results in a 0.06% reduction on the profiler.

%%WGT0{"w":[{"name":".beat1","type":"contraption","size":[88,28],"pos":[5,20],"def":".beat","widgets":{"t":{"value":"@632.49"},"utc_ts":{"value":"1695305447"},"beat_ts":{"value":"632.49"}}}],"d":{".beat":{"name":".beat","size":[88,28],"margin":[0,0,0,0],"description":"display the .beat time, also known as Swatch Internet Time.","script":"on get_ts do\n beat_ts.text\nend","template":"on beat_tick time do\n\nend","image":"%%IMG2AFgAHAACAVQAAwECIFIBAgABAQIgAQFSIAEBAyABAVQgAQECIAEBVCABAQIgAQFUIAEBAiABAVQgAQECIAEBBSACAU0gAQECIAEBBSACAU0gAQECIAEBBSACAU0gAQECIAEBBSACARQgAgE3IAEBAiABAQUgAgEUIAIBNyABAQIgAQEFIAUBAyAEAQMgAwEBIAEBASAEATYgAQECIAEBBSAGAQEgBgEBIAYBASAEATYgAQECIAEBBSACAQIgAgEBIAIBAiACAQEgAgECIAIBAiACATcgAQECIAEBBSACAQIgAgEBIAIBAiACAQEgAgECIAIBAiACATcgAQECIAEBBSACAQIgAgEBIAYBASACAQIgAgECIAIBNyABAQIgAQEFIAIBAiACAQEgAgEFIAIBAiACAQIgAgE3IAEBAiABAQUgBgEBIAYBASAGAQIgAgE3IAEBAiABAQMgAQEBIAUBAyAEAQMgAwEBIAEBAyACATYgAQECIAEBVCABAQIgAQFUIAEBAiABAVQgAQECIAEBVCABAQIgAQFUIAEBAyABAVIgAQECAAEBAiBSAQIAAwFUAAI=","attributes":{"name":[],"label":[],"type":[]},"widgets":{"t":{"type":"field","size":[54,15],"pos":[32,8],"locked":1,"animated":1,"script":"on view do\n if !(utc_ts.text~sys.now)\n  utc_ts.text:sys.now\n  d:\"%04.02f\" format ((1/86.4)*(86400%(sys.now+3600)))\n  card.event[\"beat_tick\" d]\n  beat_ts.text:d\n  me.text:\"@%s\" format beat_ts.text\n end\nend","font":"mono","show":"invert","border":0,"style":"plain","align":"right","value":"@632.44"},"utc_ts":{"type":"field","size":[47,14],"pos":[109,14],"show":"none","border":0,"value":"1695305443"},"beat_ts":{"type":"field","size":[47,13],"pos":[109,1],"show":"none","border":0,"value":"632.44"}}}}}
Developer(+1)

Nicely done!

It may seem a bit odd, but using hidden widgets is in fact the normal way to preserve state across events. Storing state in widgets means that it can always be viewed and inspected, and saving/reloading decks has straightforward, unsurprising semantics. It may be a good idea to mark such "internal" widgets in a contraption (or a deck itself) as "Show None".

Another refinement you could make is supplying a Template Script (under Prototype -> Properties...) with an empty "on beat_tick ... end" event handler; this helps make contraptions more self-documenting by indicating the events that are available and saving users typing, just like the templates Decker supplies automatically for the primitive widgets.

(+1)

Thanks for the hints - I’ve added the template script and such, and updated my post. I found that the time calculation itself could be optimised, since the call to parse "%e" sys.now and the math that followed was taking a timestamp, turning it into an object, then turning it back into a timestamp again (but where the start of the epoch is midnight of the current day, BMT), and it could be shortened to just (1/86.4) * (86400 % (sys.now + 3600)). Matters very, very little when the speed of the Lil interpreter is limited within Decker, but it feels better to me.

Developer (2 edits) (+1)

Trying to make a jump-scare, or at least a nice cupholder? Look no further than

The PopOut Contraption:

The PopOut Contraption behaves like a two-position switch, animating a configurable image in a smooth tween from one position to another when the image is clicked (or if anything else modifies its ".value" attribute). The contraption will automatically choose a vertical or horizontal orientation based on the bounding box of the contraption and the size of the source image, favoring the axis which allows the image more freedom of movement.


%%WGT0{"w":[{"name":"popOut1","type":"contraption","size":[45,84],"pos":[428,40],"script":"on click do\n popOut2.value:!popOut2.value\nend","show":"transparent","def":"popOut","widgets":{"c":{"size":[12,15],"pos":[16,0],"show":"transparent","image":"%%IMG2AAwADwABAbM="},"v":{"size":[60,11],"pos":[58,1],"value":1},"t":{"size":[64,11],"pos":[57,18],"value":"1"},"i":{"size":[66,11],"pos":[57,35]},"s":{"size":[100,11],"pos":[56,51],"value":"1.5"}}}],"d":{"popOut":{"name":"popOut","size":[101,148],"resizable":1,"margin":[0,0,0,0],"description":"A sliding animated element that alternates between two positions when clicked.","script":"on get_value do v.value end\non set_value x do v.value:x view[] end\non get_img do i.value end\non set_img x do i.value:x view[] end\non get_speed do 0+s.text end\non set_speed x do s.text:0|x end\n\non click do\n set_value[!get_value[]]\n card.event[\"click\"]\nend\non tween a b t do\n # see easeInOutBack() from easings.net\n c1:1.70158 c2:1.525\n a+(b-a)*.5*if !t>.5\n    ((2*t    )^2)*((c2+1)*(   2*t))-c2\n else\n  2+(((2*t)-2)^2)*((c2+1)*(-2+2*t))+c2\n end\nend\non view do\n img:first extract arg where arg..type=\"image\" from i.value\n c.size:img.size\n c.show:card.show\n c.paste[img]\n tn:0+t.text\n chg:if tn>v.value -1 elseif v.value>tn 1 else 0 end\n t.text:0|1&tn+chg*0.03*s.text\n c.animated:!tn~v.value\n d:card.size-c.size\n a:d[1]>d[0]\n tw:tween[2 d[a]-2 tn]\n ct:d[!a]/2\n c.pos:if a ct,tw else tw,ct end\nend","template":"on click do\n \nend","attributes":{"name":["img","speed","value"],"label":["Image","Speed","Value"],"type":["rich","number","bool"]},"widgets":{"c":{"type":"canvas","size":[26,26],"pos":[37,0],"locked":1,"border":0},"v":{"type":"button","size":[60,20],"pos":[114,2],"show":"none","style":"check","value":0},"t":{"type":"field","size":[64,20],"pos":[113,31],"show":"none","style":"plain","value":"0"},"i":{"type":"field","size":[66,20],"pos":[113,61],"show":"none","value":{"text":["","i"],"font":["",""],"arg":["","%%IMG2ABoAGgABARgAAQH/Af8BcgABARgAAQ=="]}},"s":{"type":"field","size":[100,20],"pos":[112,90],"show":"none","style":"plain","value":"1"}}}}}
Viewing posts 1 to 20 of 22 · Next page · Last page