Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

The Contraption Bazaar Sticky

A topic by Internet Janitor created Feb 28, 2023 Views: 13,991 Replies: 140
Viewing posts 1 to 21 of 36 · Next page · Last page
Developer (1 edit) (+9)

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) (+4)

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]"}}}}}
(+5)

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) (+4)

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) (+2)

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"}}}}}
(+2)

Sorry for my lack of understanding. I don't know how to change the image, how exactly do I manipulate it?
I don't understand editing prototypes.

(1 edit) (+1)

It might be easier to use the more general "Bouncer" version of this contraption:  https://itch.io/post/8380177

You can open up the properties menu of the contraption on the card where you want to use it and paste a different image into the image box:

(+2)

Thank you so much for your kindness ..!

Developer (1 edit) (+4)

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"}}}}}
(+8)


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}}}}}
(1 edit) (+1)

Hello! I was wondering how exactly one might embed widgets in the rich text field of your component. I'm very new to all this, so learning new tricks is a consistent struggle haha. But thanks for all the work y'all do making such neat stuff! And also thanks in advance for your help ;-;

(+2)

I think the behaviour you're after can be achieved by making sure the extra widget's edges are aligned properly with the Prototype's margins so that the auto-resizing behaviour still works. If you need more complicated relative positioning than that, you'll probably need to change the script of the resize widget ("drag_resize") so that it applies the transformations the same way that it currently locks and unlocks the Field

Thanks!

Something I'm a touch confused by. I wrote a simple "resize" handler for this that sets the window to fixed dimension, ie: 

on resize do
 me.size:320,240
end

This changes the window size as I expect, but when I do the window title disappears. Clicking the corner drag or moving it gets the title back. Is there some extra step I need to invoke to make sure the title draws after the resize? I tried just calling "view[]"/"me.view[]" but that wasn't it.

Developer(+1)

This contraption wasn't designed with that in mind, but it's an easy modification. The script for the "resize" button in the prototype:

on click do
 card.event["resize"]
end

Needs to call the prototype-level view[] event handler to reposition/resize the window title after user code has had a chance to run:

on click do
 card.event["resize"]
 view[]
end

The "close" button requires a similar tweak.

Here's a paste-able version with both modifications:

%%WGT0{"w":[{"name":"macFieldWindow1","type":"contraption","size":[109,105],"pos":[235,38],"script":"on close do\n \nend\n\non resize do\n me.size:320,240\nend","def":"macFieldWindow","widgets":{"close":{},"resize":{"pos":[88,4]},"title":{"value":"Hello"},"drag_resize":{"pos":[93,89]},"field":{"size":[108,71],"value":{"text":["I am a draggable and resizeable window sporting a rich text field!\n","Try me!"],"font":["","menu"],"arg":["",""]}},"bar":{"size":[109,19]}}}],"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","version":0.1,"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\"]\n view[]\nend","style":"invisible"},"resize":{"type":"button","size":[11,11],"pos":[38,4],"script":"on click do\n card.event[\"resize\"]\n view[]\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,"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,"draggable":1,"scale":1}}}}}
(+2)

Ah! So needed to go up the chain. Works great now, thanks!

Useful trick I discovered. I have a page with a bunch of character portraits, and then you click on them to make a macWindow popup with a bio. I just have the one macWindow, and the button script updates the text and unhides the window if you closed it.

The problem is that when you've scrolled down in one bio, even after closing and reopening, it still retains the old scroll position, so you end up jumped in the middle of the second bio. Easy fix for this was to modify the `set_value` method to reset the scroll:

on set_value x do 
 field.value: x 
 field.scrollto[0]
end

Hello! I know it seems a bit too late to ask? :')) But, for some reason I can't seem to get the contraption to close whenever I click the (left) corner button and im not sure how to fix it. (Im still kinda new to Decker.)

Developer(+2)

The "close" and "resize" buttons of this contraption don't do anything by default, but you can add your own behaviors with scripting.

If you open the properties panel for the MacWindow (double click on it while in Widgets mode) and click "Script..." you'll see the template script of the contraption:

on close do
end
on resize do
end

Fill in "on close ..." to make it hide the contraption:

on close do
 me.show:"none"
end

It's easy to un-hide the window again later in another script:

myMacWindow.show:"solid"

If you really want to destroy the contraption instance when you click the "close" button you can remove it from the deck:

on close do
 deck.remove[me]
end

Does that make sense?

(+2)

Ohh okay! Got it. Thank you for the quick feedback!

Developer(+3)

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"}}}}}

hi there! if i replaced everything after:

"on finish"

with:

"go[card]"

would i be able to have a project that automatically jumps the viewer to a new card once the countdown reaches 0? thank you very much for this program by the way, it's great!

Developer(+1)

Yes. Just to be clear, if you make an instance of the EggTimer contraption and edit it's script, it will have a template like

on finish do
end

You can then fill in an event handler for "finish" like you would any script. For example,

on finish do
 go["someCardName"]
end

Note that if the only thing you want to do is wait 10 seconds after clicking a button before navigating to another card you could use the sleep[] function on an ordinary button's script:

on click do
 sleep[10 * 60]
 go["someCardName"]
end

The difference being that sleep[] is "synchronous" and prevents the user from doing anything else while the script sleeps, whereas the EggTimer is "asynchronous" and the user could click something else or navigate away from the timer before it goes off.

ah, thank you for this. i'd rather use this asynchronous option because i aim to have the player click on elements of an image that either pop-up things or navigate to other cards while the countdown is still actively going on (not quite sure yet, but it's a work in progress).

I placed eggtimers on a bunch of cards, and then wanted to loop through them and change the Seconds value. I've done this in the Listener with Field widgets, but how do I do it with eggtimers? This doesn't seem to work, to change it for a single eggtimer:

eggtimer1.event["set_seconds" 180]
Developer (1 edit) (+1)

The attribute exposed externally is `seconds`. The internal accessor/mutator functions are therefore named `get_seconds` and `set_seconds`, respectively. From the outside, you can modify the attribute like

eggtimer1.seconds:180
(+1)

Ah, it's shown under Prototype/Attributes...

I managed to look everywhere else.

Is there a way to automatically start the egg timer when a player goes to another page so there's no need for player intervention, but the page is still interactable? I tried calling eggtimer on click function on the onview function on a card it's currently at, but it doesn't seem to work. Would love the help thanks! 

(+1)

If you just want a timer and it doesn't need to be this "egg timer" specifically.... 

Making timers from scratch comes up from time to time, and it's pretty easy. 

Here's a small example in the programming questions thread that would start automatically when the card is viewed. 

And here's another thread talking about making timers.

If that seems like something that would work as a replacement for what you wanted the EggTimer Contraption for, then I'm very sure folks around here would be happy to answer questions to make sure your home made timer does what you want it to do.

(+2)

Oh my god you're a life saver thank you!

Developer (1 edit) (+5)

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) (+5)

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="]}}}}}}

您好,我是 decker 的新手。我想问一下是否有办法将图像直接拖动到画布中。我只是想为视觉小说制作一个木偶,但我不想在 decker 中再次绘制它。

(+1)

您好!我录制了将图像复制到画布的过程。
Hello! I recorded the process of copying an image into a canvas.

该过程包括复制图像数据,然后在 Widget 模式下创建新画布。
The process involves copying the image data, and then creating a new canvas while in Widget mode.

“Paste as new Canvas”(粘贴为新画布) 是在 Widget 模式下可用的菜单选项。
"Paste as new Canvas" is the menu option that becomes available in Widget Mode.

或者,您可以使用 Decker “Toolbars”(工具栏) 更快地切换到 Widget 模式。
Optionally, you can use the Decker "Toolbars" to change to Widget mode more quickly.


该录制还使用菜单选项“Tight Selection”(紧密选择) 来消除图像周围的空白空间。
This recording also uses the option "Tight Selection" to remove empty space around the image.

希望对您有所帮助。
I hope this helps.

如果您希望获得更多项目方面的帮助,请随时在论坛中创建新主题。当问题出现时,我们很乐意帮助解答。
Please feel free to create a new topic in the forums for more help on your project. It would be a pleasure to help answer questions as they come up.


(本文为机器翻译,如有错误,敬请谅解。)
   (This was machine translated. I'm sorry for any errors.)

(+2)

Thank you so much for recording the process video! It has helped me a lot, and now I can use it to create more exciting animation performances! I apologize for replying so late because I am not often active in the community, but I still want to thank you very much!!!

Developer (2 edits) (+8)

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!","version":1,"script":"on view do\n fr:f.images\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\non get_animate do view end","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!

(+1)

Hello, I've been poking around in the wonders of decker recently and reading up as much as I can, but I couldn't figure out how to use this contraption for colored gifs. Is there a way, and if so, do you think you could help? Thanks!

Developer (2 edits) (+1)

The "gif" contraption is designed to read gifs as a series of grayscale frames and dither them.

We could make it handle color by replacing that dithering code, which is in the script of the "b" button,

 g:"[255,0,255,246,148,108,132,51,61,147,144,86,66,111,185,134,69,0]"
 grays:(0,1,32+range 16) dict "%j" parse g
 f.value:raze each i in read["image" "frames"].frames
  rtext.make["" "" i.map[grays].transform["dither"]]
 end

With something based on the equivalent in the "scrubber" contraption:

 f.value:raze each i in read["image" "frames"].frames
  rtext.make["" "" i]
 end

All together as a copyable snippet:

%%WGT0{"w":[{"name":"colorgif1","type":"contraption","size":[100,100],"pos":[206,121],"def":"colorgif","widgets":{"c":{},"f":{},"b":{}}}],"d":{"colorgif":{"name":"colorgif","size":[100,100],"resizable":1,"margin":[0,0,0,0],"description":"Import and play animated 16-color gifs. Careful: huge gifs can quickly bloat the size of your deck!","version":1,"script":"on view do\n fr:f.images\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\non get_animate do view end","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 f.value:raze each i in read[\"image\" \"frames\"].frames\n  rtext.make[\"\" \"\" i]\n end\n view[]\nend","text":"Open Gif..."}}}}}

Note that this is still ultimately limited by Decker's 16-color palette; colors not in the palette are converted into the current palette via a minimum-squared-distance heuristic, which doesn't always do a fantastic job.


For best results you might need to customize Decker's palette and/or use external tools like imagemagick to adjust the GIF's palette. If you just want a little motion and color in your decks, you could instead directly import the "wiggler" contraption from wigglypaint.

Does any of that help point you in the right direction?

(+1)

Oh this should be perfect, yes, thank you! I had already read up on the limited palette and spent a few good hours figuring imagemagick out to adjust gifs, so that should be easy enough now. Thank you kindly!

How could I set the gif to start from beginning when I go to the card it's in?

I wanna use it for a non-looping video with a timer that changes to the next card, but right now the gif might be playing from the middle when I open the card it's in.

(+1)

If you don't mind a slightly lateral answer, this is what's inside the gif contraption:


There is a Button that contains a script that imports each frame of a gif and stores them all as image data in a hidden rich text field.
There is the Field that is storing all of the imported image data in sequence.
There is a Canvas that the images will be displayed on.
And there is a Script which takes each frame from the image data in the Field and displays them on the Canvas in a loop.

When there's a contraption that's almost what I want I often look inside to take out a specific piece that I need, and combine it with other things to get a different effect.

In your case the import button and the frame storage already should work fine but the playback script seems to need to be a little different for your project. 

From what you've said your clip should always start at the beginning when someone arrives at the card, and the player/viewer should be sent somewhere else at the same time that the playback ends, right?

If you'd be comfortable making a new thread about what you're trying to do I'm sure folks would be happy to help you get a really specific answer for your project (other people have done more things with video playback in Decker than I have!) and it would probably be a helpful discussion for the community.

(+1)

We ended up making animations by just having each frame on a separate card instead of importing a gif, but thanks a ton for the answer! I'm very new to Decker so I had a hard time trying to understand how the gif contraption works and what I'm supposed to edit in it. Your breakdown makes sense!

Developer(+3)

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?

(2 edits) (+1)

I have been using this in a bunch of places in my current deck, and I have somehow managed to break it?

It suddenly fails to update the text when you click the buttons. If I switch modes, from interact to widgets and back, then it updates to the correct value, so the value property is clearly still working, it's just the display part that isn't.

I tried repasting in the widget from the post, in case I'd some how munged it while skimming the prototype code for ideas, but it's still not working. I had something like this happen once before and it was only temporary, I think I fixed it by just quitting without save and reloading the deck, but whatever i did was enough saves and commits back as to make no difference now.

EDIT: Figured it out. The internal slider in 'enum' has no default script, but if you go opening its script, Decker helpfully adds an empty "change" event from the default template for sliders ... for reasons I don't entirely think I understand, this breaks the prototype. Manually deleting that "change" script fixes it again. 🤷🏼‍♀️

Developer(+2)

I'd just like to say that I really appreciate you leaving your original comments and following up when you're able to resolve problems on your own; it provides very valuable information for other users who are reading along.

If a widget defines no event handler for an event, the event "bubbles up" to the container card or contraption, and in the case of a card it can further "bubble up" to the deck-level script. Events which "bubble" still further are handled by default event handlers, if any exist. I take advantage of this in many of my contraptions because there is often only a single widget that can produce e.g. a "drag" or "change" event and putting all the logic in the contraption-level script where i can see it at once is slightly more convenient.

An existent but empty event hander "swallows" the event and does not give higher-level definitions a chance to do their work. I recognize that event handler "templates" can be a bit of a footgun in this case, but there isn't any way Decker can automatically distinguish "as-of-yet unfilled" handlers from intentionally empty event handlers meant to suppress bubbling.

I'd also like to note that re-pasting a contraption definition into a deck where the original has been modified will not update existing contraption instances unless the new definition has a higher version number.

Does that help clarify?

(+2)

Yeah, that makes sense, and also explains something I noticed while working with canvas widgets: they also stop working once you open their script, because it stubs empty handlers that then replace the default drawing behavior. 

The insurance policy I put in for 'enum' was to replace the empty handler with a comment instead, as once a script is populated that suppresses the template filling behavior, but in the future now that I understand what causes it, I'll at least know where to look.

And for sure! I think its very important for people to be able to bungle on main, 'cause everyone else gets to learn. Glad its been helpful. ^^

Developer (1 edit) (+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":[52,131],"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.","version":1,"script":"on is_valid  x do 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 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 err\n  err\n else\n  if !silent card.event[\"change\" x] end\n  \"\"\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"}}}}}

I'm super new to Decker and trying to use the validator contraption as a way to implement a specific password in order to be able to advance to the next card. I'm not sure if that is the intended function of this contraption or a possibility and would greatly appreciate any guidance.

Developer

It would probably be just as easy to use a plain field as a Validator for what you describe. Say you have a field named "myfield". You could then have a button with a script something like:

on click do
 if myfield.text ~ "the password"
  go[someOtherCard]
 end
end

Or perhaps you could give the field a script and enable a button when it contains the correct text?

on change val do
 myButton.locked: !val~"the password"
end

This thread has some additional discussion about implementing a password/search system that might be useful.

(+1)

Thanks for the quick feedback! I shall try to implement both of those solutions, if only to get a better grasp on the system and figure out which one works best. I will also check out the linked thread. Thanks again!

Developer (1 edit) (+3)

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) (+2)

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) (+1)

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"}}}}}
(9 edits) (+3)

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 (now updated to Version 4.2)!

%%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","version":4.2,"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_animate do\n animate\nend\n\n#this is called by the view handler on the frame counter\non animate do\n if playing.value\n  tempo:(first extract value where param=\"tempo\" from synthparams.value)+0\n  if !((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     card.event[\"playline\" notes.row]\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    card.event[\"playline\" notes.row]\n    calcnextrow[]\n   end\n  end\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\n\n\non startplayback do\n playstop.text:\"Stop\"\n #calculate row into buffer\n calcrow[]\n lastframe.value:sys.frame\n playbuffer[]\n card.event[\"playline\" notes.row]\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 ((x=(a+d)) * (s*v)) +\n ((((a+d)<x) & (x<(a+d+l))) * (s*v)) + #sustain\n ((x=(a+d+l)) * (s*v)) +\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\n\non playline row 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 alert[\"Select the synth parameters CSV\"]\n synthparams.value:readcsv[read[\"text\"]]\n alert[\"Select the pitches CSV\"]\n 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 click do\n playfromstart[]\nend","show":"none","text":"Play from start"},"playstop":{"type":"button","size":[60,20],"pos":[120,7],"locked":1,"script":"on click do\n 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 do\n \nend\n\non view do\n animate[]\nend","value":"0"},"buffer1":{"type":"field","size":[100,20],"pos":[-139,-105],"locked":1,"volatile":1},"buffer2":{"type":"field","size":[100,20],"pos":[-15,-105],"locked":1,"volatile":1},"buffer3":{"type":"field","size":[100,20],"pos":[110,-106],"locked":1,"volatile":1},"buffer4":{"type":"field","size":[100,20],"pos":[232,-104],"locked":1,"volatile":1}}}}}

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

(+1)

And another update to version 4, that adds a few new features

Developer (1 edit) (+5)

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) (+4)

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) (+5)

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"}}}}}
(+2)

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

Developer(+3)

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) (+4)

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(+2)

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) (+2)

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"}}}}}
Developer

PSA: several of the above contraption definitions were impacted by changes in Decker 1.32, and have been updated accordingly. If you're working on a deck that contains any of them (bouncer, eye, bob, pulse, rotor, seekRotor, interior, popOut), please refer to the release notes for help migrating.

Viewing posts 1 to 21 of 36 · Next page · Last page