Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

[script REQ] Aaron's Roll20 Enhancement pack

1440540195

Edited 1705693455
vÍnce
Pro
Sheet Author
UPDATED: 1/19/24' Just some "un-official" scripts or snippets from The Aaron (and a ton of official ones here ) that I've gathered from the forums over the years. I started this thread over 9 years ago! Some of these may no longer work. I've added newer scripts to the bottom of the list and will try and update others as needed. Enjoy. &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; Center-Align Small Tokens: &nbsp; snap token's smaller than 70px to center of grid. <a href="https://app.roll20.net/forum/permalink/2343022/" rel="nofollow">https://app.roll20.net/forum/permalink/2343022/</a> on('ready', function () { 'use strict'; on('change:graphic', function (obj, prev) { if ( _.contains(['gmlayer', 'objects'], obj.get('layer')) &amp;&amp; !obj.get('isdrawing') &amp;&amp; (obj.get('left') !== prev.left || obj.get('top') !== prev.top) &amp;&amp; (obj.get('width') &lt; 70 || obj.get('height') &lt; 70) ) { obj.set({ left: obj.get('left') + 35 - obj.get('width') / 2, top: obj.get('top') + 35 - obj.get('height') / 2 }); } }); }); Master Map Default:&nbsp; On load, it will look for a page named MASTER (which can be archived). When a new page is created, it will copy all the settings from the MASTER page to the newly created one. <a href="https://app.roll20.net/forum/permalink/2343047/" rel="nofollow">https://app.roll20.net/forum/permalink/2343047/</a> on('ready', function() { "use strict"; var masterPage = findObjs({ type: 'page', name: 'MASTER' })[0]; if (masterPage) { on('add:page', function(p) { p.set({ showgrid: masterPage.get('showgrid'), showdarkness: masterPage.get('showdarkness'), showlighting: masterPage.get('showlighting'), lightupdatedrop: masterPage.get('lightupdatedrop'), lightenforcelos: masterPage.get('lightenforcelos'), lightrestrictmove: masterPage.get('lightrestrictmove'), lightglobalillum: masterPage.get('lightglobalillum'), width: masterPage.get('width'), height: masterPage.get('height'), snapping_increment: masterPage.get('snapping_increment'), grid_opacity: masterPage.get('grid_opacity'), fog_opacity: masterPage.get('fog_opacity'), background_color: masterPage.get('background_color'), gridcolor: masterPage.get('gridcolor'), grid_type: masterPage.get('grid_type'), scale_number: masterPage.get('scale_number'), scale_units: masterPage.get('scale_units'), gridlabels: masterPage.get('gridlabels'), diagonaltype: masterPage.get('diagonaltype') }); }); } else { sendChat('MasterPage', '/w gm No page named MASTER found.'); } }); Show Portrait/Avatar in Chat: show a character/journal avatar in chat. <a href="https://app.roll20.net/forum/permalink/1548991/" rel="nofollow">https://app.roll20.net/forum/permalink/1548991/</a> on('ready', function () { on('chat:message', function (msg) { if (msg.type == 'api' &amp;&amp; msg.content.indexOf('!char-pic') !== -1) { var charid = msg.content.split(' ')[1]; var c = getObj('character', charid); if (c) { var fPart = "&lt;div style='box-shadow: 3px 3px 2px #888888; font-family: Verdana; text-shadow: 2px 2px #000; text-align: center; vertical-align: middle; padding: 1px 1px; margin-top: 0.1em; border: 1px solid #000; border-radius: 8px 8px 8px 8px; color: #FFFFFF;"; var tPic = fPart + "background-color:#666666;'&gt;● " + c.get('name') + ' ●&lt;/div&gt;'; var Pic = fPart + "background-color:#AAAAAA;'&gt;&lt;img src='" + c.get('avatar') + "'&gt;&lt;/div&gt;"; sendChat('', '/direct ' + tPic + Pic); } } }); }); Example macros: &nbsp;!char-pic @{Bob|character_id} or&nbsp;!char-pic @{target|Who?|character_id} Resize Turn Tracker: a per-session bookmarklet that reduces the size of the turn tracker. <a href="https://app.roll20.net/forum/permalink/2317121/" rel="nofollow">https://app.roll20.net/forum/permalink/2317121/</a> javascript:(function(){ $('#initiativewindow .characterlist .name').hide();$('#initiativewindow').parent().css('width','100px'); })(); Here's one that sets it back: javascript:(function(){ $('#initiativewindow .characterlist .name').show();$('#initiativewindow').parent().css('width','200px'); })(); You can actually do this locally via a Bookmarklet: For Chrome, you just create bookmarks with the above in them. During the game, you choose the one you want and it makes the changes. These changes are local and only persist until you reload. [Stylish] 4-Column Initiative with 1 column for editing!: make the Turn Order 4 columns, but when you go to manipulate it, it reverts to a single column. <a href="https://app.roll20.net/forum/permalink/5230924/" rel="nofollow">https://app.roll20.net/forum/permalink/5230924/</a> /* Comment out this part to have single columns... */ #initiativewindow .characterlist &nbsp; &nbsp; { &nbsp; &nbsp; -moz-column-count: 4; &nbsp; &nbsp; -moz-column-gap: 20px; &nbsp; &nbsp; -webkit-column-count: 4; &nbsp; &nbsp; -webkit-column-gap: 20px; &nbsp; &nbsp; column-count: 4; &nbsp; &nbsp; column-gap: 20px; } #initiativewindow .characterlist:hover &nbsp; &nbsp; { &nbsp; &nbsp; -moz-column-count: 1; &nbsp; &nbsp; -webkit-column-count: 1; &nbsp; &nbsp; column-count: 1; } /* ...down to here. */ #initiativewindow .characterlist .token .name{ font-size: 1em !important; } #initiativewindow .characterlist .token .initiative{ font-size: 1em !important; background-color: #ccc; &nbsp; &nbsp; font-weight: bold; &nbsp; &nbsp; border-radius: .5em; } #initiativewindow { &nbsp; &nbsp; padding: 1px 1px; &nbsp; &nbsp; overflow-x: auto; &nbsp; &nbsp; position:relative; } #initiativewindow ul li { &nbsp; &nbsp; padding: 1px; &nbsp; &nbsp; white-space: nowrap; } #initiativewindow ul li { &nbsp; &nbsp; min-height: 15px } #initiativewindow ul li img { &nbsp; &nbsp; max-width: 20px; &nbsp; &nbsp; max-height: 15px; } #initiativewindow ul li .controls { font-size: 15px; &nbsp; &nbsp; padding: 1px; &nbsp; &nbsp; } #initiativewindow ul li span.initiative { &nbsp; &nbsp; font-size: 18px; &nbsp; &nbsp; padding: 2px; &nbsp; &nbsp; min-height: 12px; &nbsp; &nbsp; z-index: 1000; &nbsp; &nbsp; position:relative; } #initiativewindow ul li span.name { &nbsp; &nbsp; font-size: 14px; &nbsp; &nbsp; padding-top: 2px; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } #initiativewindow input { &nbsp; &nbsp; font-size: 15px; &nbsp; &nbsp; height: 15px; } .ui-dialog-titlebar { &nbsp; &nbsp; padding: 2px !important; } .ui-dialog-titlebar .ui-dialog-title { &nbsp; &nbsp; margin: 0 !important; &nbsp; &nbsp; font-size: 10px; &nbsp; &nbsp; line-height: 10px; } .ui-dialog-buttonpane { &nbsp; &nbsp; margin: 0 20px 0 0 !important; &nbsp; &nbsp; padding: 1px !important; } .ui-button { &nbsp; &nbsp; font-size: 10px !important; &nbsp; &nbsp; line-height: 10px !important; &nbsp; &nbsp; padding: 1px 3px !important; } .ui-button.bigbuttonwithicons .ui-button-text { &nbsp; &nbsp; font-size: 10px !important; } Here is what it looks like with those first two styles commented out so that it has a single column: Note, this will also affect the size of headers and buttons on other dialogs, like dealing cards, designing decks, making rollable tables, etc.. Highlight Audio Playing Jukebox: &nbsp; a per-session bookmarklet that highlights all currently playing tracks in the audio tab. <a href="https://app.roll20.net/forum/permalink/1936428/" rel="nofollow">https://app.roll20.net/forum/permalink/1936428/</a> javascript:(function(){_.each($('#jukebox').find('tr'),function(r){var $r=$(r),$b=$($r.find('button'));if($b.hasClass('pause')){$r.css({backgroundColor: '#ff0000'});}});}()); Just make a bookmark with that as the URL. When you click it while on the Roll20 VTT, it will change the background of any playing tracks to bright red. =D The highlight goes away whenever you click a button. Clear UI: a per-session bookmarklet that that hides the UI. <a href="https://app.roll20.net/forum/permalink/1176273/" rel="nofollow">https://app.roll20.net/forum/permalink/1176273/</a> Ok. Here's a JavaScript Bookmark-let that will hide all the UI for you. Just make a bookmark in Chrome (should work in FF too, but didn't try it there) with a title like " Roll20: Clear UI " and the URL like this: javascript:(function(){$("#zoomslider").css("display","none");$("#floatingtoolbar").css("display","none");$("#sidebarcontrol").css("display","none").click();$("#playerzone").css("display","none");$("#initiativewindow").parent().css("left","-10000px")})(); Alternate version used on GM view(thanks Pringle) All credit still to The Aaron (this change is tiny), but if you want to use this on a GM view and hide the blue tab then use: javascript:(function(){$("#zoomslider").css("display","none");$("#floatingtoolbar").css("display","none");$("#sidebarcontrol").css("display","none").click();$("#playerzone").css("display","none");$("#initiativewindow").parent().css("left","-10000px");$("#page-toolbar").css("display","none")})(); Expand Page Toolbar: &nbsp; a per-session bookmarklet that sets the height and prevents wrapping of the page toolbar. <a href="https://app.roll20.net/forum/permalink/1081664/" rel="nofollow">https://app.roll20.net/forum/permalink/1081664/</a> Make a bookmark in your browser, but put this in the url: javascript:(function(){ $('#page-toolbar').height('800px');$('#page-toolbar .container').css({height: '800px','white-space': 'normal'});}()); When you are on the VTT and choose it, it will make the page bar display much taller and wrapping. You won't be able to sort pages with it like this, but you can grab the player flag and move it to any of the displayed pages (the flag stays on the top row, but it will move the players to the page you highlight with the blue border.). &nbsp;Refreshing the page will restore the page bar to normal. Increase Font-size for Chat: a per-session bookmarklet that increases the font-size of chat. <a href="https://app.roll20.net/forum/permalink/1276072/" rel="nofollow">https://app.roll20.net/forum/permalink/1276072/</a> javascript:$('#textchat').css('font-size','1.5em'); Just make a bookmark in chrome, but put the above as the URL. When you're on the VTT, select that bookmark and it will change all the text in the chat area to the 1.5em size (which is about 50% larger). When you refresh the page, it will go back to normal. You can change the 1.5em to whatever size you like. =D Sort Transmogrifier list: <a href="https://app.roll20.net/forum/permalink/4891027/" rel="nofollow">https://app.roll20.net/forum/permalink/4891027/</a> javascript:$('iframe').contents().find('.objects').each((c,e)=&gt;{ let $e=$(e); $e.children().sort( (a,b)=&gt;{ let name1=$(a).find(".name").text().toLowerCase(), name2=$(b).find(".name").text().toLowerCase(), comp = name1.localeCompare(name2); return comp; }) .each((i,c)=&gt;$e.append(c)); });&nbsp; This is a bookmarklet that will sort your transmogrifier lists. In Chrome, just make a new bookmark with the above as the link. When you have the transmogrifier open and select this link, it will sort the currently open lists. You'll need to run it again when you choose a new list or after dragging things across to get the list back in order. Cycle thru Token Ability Macros: <a href="https://app.roll20.net/forum/permalink/4891568/" rel="nofollow">https://app.roll20.net/forum/permalink/4891568/</a> javascript:(function(){ "use strict"; $('body').keydown(function(e) { var n,o,t; if( e &amp;&amp; e.altKey &amp;&amp; (48 &lt;= e.keyCode) &amp;&amp; (58 &gt;= e.keyCode) ){ n=e.keyCode - ( 48===e.keyCode ? 38 : 49 ); o=$('#secondary-toolbar ul.mode.tokenactions'); if(o &amp;&amp; 'none' !== o.css('display')) { t=o.children()[0].children[n]; if(t) { t.click(); } } } }); }()); It lets you hit alt-1 through alt-0 to activate one of the first 10 Token Action buttons on the selected token. Copy ALL API Scripts: <a href="https://app.roll20.net/forum/permalink/2995136/" rel="nofollow">https://app.roll20.net/forum/permalink/2995136/</a> You can use this JavaScript Bookmark-let to build a text area with all the scripts concatenated: javascript:$('&lt;textarea&gt;&lt;/textarea&gt;').attr({id: 'TheAaronAllScripts'}).css({width:'100%',height: '30em'}).text($('.script .editor').map(function(idx,e){return e.env.editor.getValue();}).toArray().join("\n/* ############################### */;\n\n")).appendTo('body'); The above you can copy and put in a bookmark in Chrome. For Firefox, you need a link to right click and add bookmark, so far as I know. This might work to copy in Firefox: <a href="http://javascript:$(' Bookmark-let Copy Only ACTIVE API Scripts: https://app.roll20.net/forum/permalink/5019380/ javascript: (function(){let disabledScripts=$('ul.nav.nav-tabs li a.disabled').map((idx,e)=&gt;$(e).attr('href').substr(1));$('&lt;textarea&gt;&lt;/textarea&gt;').attr({id: 'TheAaronAllScripts'}).css({width:'100%',height: '30em'}).text($('.script.tab-pane').filter((idx,e)=&gt;!_.contains(disabledScripts,$(e).attr('id'))).map((i,e)=&gt;$('.editor',e)[0]).map(function(idx,e){return e.env.editor.getValue();}).toArray().join("\n/* ############################### */;\n\n")).appendTo('body'); }()); User Library Image Download Helper: https://app.roll20.net/forum/permalink/5153640/ Here is a bookmark-let that will work with the Recent Uploads Dialog: javascript:(function(){ var links=$('#libraryview .library-container').map(function(idx,block){ var $block=$(block), img=$block.find('img'),name=$block.find('.library-labelcontainer span').text(), ext = img.attr('src').match(/\b(?:thumb|max|original)\b\.(\w*)/)[1]; return $('&lt;a style="border:1px solid #999;float:left;display:block;width:52px;height:52px;" href="'+img.attr('src').replace(/\b(?:thumb|max)\b/,'original')+'" download="'+name+'.'+ext+'"&gt;&lt;img style="max-width:50px;max-height:50px;" src="'+img.attr('src').replace(/\b(?:thumb|max)\b/,'original')+'"&gt;&lt;/a&gt;').click(function(){$(this).css('opacity',0.5);});}).toArray(); $('&lt;div style="position:absolute;width:100%;height:100%;overflow-y:auto;top:0;left0;z-index:1000000;background-color: #ccc;"&gt;&lt;/div&gt;').html(links).appendTo('body');}()); In chrome, make a bookmark with that as the link named something like "User Library Image Download Helper". In one of your games, Click a recent upload image (1), to open the Recent Uploads Dialog (2), then scroll down until it's done loading images (3): Once you don't see the "Loading Recent Uploads..." thing, select that created bookmark. It will then build a grid of all your user library images. This might take a few minutes, as the thumbnail sized images are really the full size originals. Each of those is a clickable link to the original image. When I originally wrote a version of this for the external user library, you could click each one and download the image easily. That wasn't working for me in latest chrome in this version, so you might have to right click and download each image (clicking the image will grey it out, so you can use that to keep your place. Unfortunately, they'll all be named original something, so you'll have to rename as you go or not worry about the name. YMMV. Campaign "Born on" date script: https://app.roll20.net/forum/permalink/5197532/ Will give you it's best guess about the creation date of your game. If the game is olde enough, it might output garbage.. YMMV... on('ready',function(){ &nbsp; &nbsp; let createdOn=new Date(_.chain(getAllObjs()) &nbsp; &nbsp; &nbsp; &nbsp; .map((o)=&gt;o.id) &nbsp; &nbsp; &nbsp; &nbsp; .reject((id)=&gt;id==='root') &nbsp; &nbsp; &nbsp; &nbsp; .map((id)=&gt;_.reduce( (id||'').substring(0,8).split(''), (m,c)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (m*64)+("-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".indexOf(c)); &nbsp; &nbsp; &nbsp; &nbsp; },0)) &nbsp; &nbsp; &nbsp; &nbsp; .sortBy(_.identity) &nbsp; &nbsp; &nbsp; &nbsp; .first() &nbsp; &nbsp; &nbsp; &nbsp; .value()||0*1000); &nbsp; &nbsp; sendChat('Created on Date (Best Guess)',`/w gm ${createdOn}`); }); GM is rolling script: https://app.roll20.net/forum/permalink/5790953/ If you want a script for it, you could use this: on('ready',()=&gt;{ on('chat:message',(msg)=&gt;{ if('gmrollresult'===msg.type &amp;&amp; playerIsGM(msg.playerid)){ sendChat('...','the GM is rolling...'); } }); }); if the GM uses /gmroll or /gr&nbsp; Re-Number Tokens mod: https://app.roll20.net/forum/permalink/11725508/ Here's one that's a little smarter. It will rebuild the names of tokens if they represent a character, and number individually all the tokens representing that character. !renumber-selected on('ready',()=&gt;{ on('chat:message',msg=&gt;{ if('api'===msg.type &amp;&amp; /^!renumber-selected(\b\s|$)/i.test(msg.content) &amp;&amp; playerIsGM(msg.playerid)){ let args = msg.content.split(/\s+/); let numBase = (parseInt(args[1])||1)-1; let tcmap = (msg.selected || []) .map(o=&gt;getObj('graphic',o._id)) .filter(g=&gt;undefined !== g) .reduce((m,t)=&gt;{ const rep=t.get('represents')||'%%NOCHAR%%'; m[rep]=m[rep]||[]; m[rep].push(t); return m; },{}); Object.keys(tcmap).forEach( c =&gt; { let ts = tcmap[c]; let n = numBase; let numer = (t) =&gt; { let baseName = t.get('name').replace(/\s+\d*$/,''); t.set('name', `${baseName} ${++n}`); }; if('%%NOCHAR%%' !== c){ // number character tokens let character = getObj('character',c); if(c) { numer = (t) =&gt; { t.set('name', `${character.get('name')} ${++n}`); }; } } ts.forEach(numer); }); } }); }); Rando Token Image: https://app.roll20.net/forum/permalink/11763269/ Should randomly change the face of any token added to the tabletop which isn't a player token. on('ready',()=&gt;{ const playerCanControl = (obj, playerid='any') =&gt; { const playerInControlledByList = (list, playerid) =&gt; list.includes('all') || list.includes(playerid) || ('any'===playerid &amp;&amp; list.length); let players = obj.get('controlledby') .split(/,/) .filter(s=&gt;s.length); if(playerInControlledByList(players,playerid)){ return true; } if('' !== obj.get('represents') ) { players = (getObj('character',obj.get('represents')) || {get: function(){return '';} } ) .get('controlledby').split(/,/) .filter(s=&gt;s.length); return playerInControlledByList(players,playerid); } return false; }; const getCleanImgsrc = (imgsrc) =&gt; { let parts = imgsrc.match(/(.*\/images\/.*)(thumb|med|original|max)([^?]*)(\?[^?]+)?$/); if(parts) { return parts[1]+'thumb'+parts[3]+(parts[4]?parts[4]:`?${Math.round(Math.random()*9999999)}`); } return; }; const isString = (s)=&gt;'string'===typeof s || s instanceof String; let tokenIds = []; const saveTokenId = (obj) =&gt; { tokenIds.push(obj.id); }; const setTokenRandomSide = (obj,prev,force=false) =&gt; { if(tokenIds.includes(obj.id) || force){ tokenIds=tokenIds.filter(id =&gt; id !== obj.id); if( 'graphic' === obj.get('type') &amp;&amp; 'token' === obj.get('subtype') &amp;&amp; ! playerCanControl(obj)){ let sideText = obj.get('sides'); if( sideText.length ){ let sides = sideText.split(/\|/).map(decodeURIComponent).map(getCleanImgsrc).filter(isString); if(sides.length){ obj.set({ imgsrc: sides[randomInteger(sides.length)-1] }); } } } } }; on('add:graphic', saveTokenId); on('change:graphic', setTokenRandomSide); }); Macro-Cleanup: https://app.roll20.net/forum/permalink/11763547/ And hit the&nbsp; ❌ &nbsp;next to a player name to remove all their macros.&nbsp; You can also remove individual macros with the&nbsp; ❌ &nbsp;next to them. !macro-cleanup code: on('ready',() =&gt; { // ❌ const s = { button: `font-size: .6em; border: 1px solid #999;background-color: #ccc; border-radius: 1em; padding: .1em .3em;min-width:1.5em;text-align:center;`, tokenAction: `background: green; font-weight: bold; border: 1px solid #333; color: white;` }; const playerName = (()=&gt;{ let players = findObjs({type:'player'}) .reduce((m,p)=&gt;({...m, [p.id]: p.get('displayname')}),{}); return (pid) =&gt; players[pid] || '[Unknown]'; })(); const f = { li: (c) =&gt; `&lt;li&gt;${c}&lt;/li&gt;`, ul: (c) =&gt; `&lt;ul&gt;${c}&lt;/ul&gt;`, ol: (c) =&gt; `&lt;ol&gt;${c}&lt;/ol&gt;`, b: (c) =&gt; `&lt;b&gt;${c}&lt;/b&gt;`, subhead: (c) =&gt; `&lt;h4&gt;${c}&lt;/h4&gt;`, ta: () =&gt; `&lt;span style="${s.tokenAction}"&gt;TA&lt;/span&gt;`, button: (label,command) =&gt; `&lt;a style="${s.button}" href="${command}"&gt;${label}&lt;/a&gt;`, mac: (m) =&gt; `${f.b(m.get('name'))} ${m.get('istokenaction') ? f.ta() : ''} ${f.button(`❌`,`!macro-cleanup ${m.id}`)}`, pla: (p,ms) =&gt; `${f.subhead(`${playerName(p)} ${f.button(`❌`,`!macro-cleanup ${ms.map(m=&gt;m.id).join(' ')}`)}`)}${f.ul(ms.map(f.mac).map(f.li).join(''))}`, tree: (tree) =&gt; Object.keys(tree).map(pid=&gt;f.pla(pid,tree[pid])).join('') }; on('chat:message', (msg) =&gt; { if('api' === msg.type &amp;&amp; /^!macro-cleanup(\b\s|$)/i.test(msg.content) &amp;&amp; playerIsGM(msg.playerid) ){ let who = (getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); let args = msg.content.split(/\s+/).slice(1); if(args.length){ _.chain(args) .map((id)=&gt;getObj('macro',id)) .reject(_.isUndefined) .tap((ms)=&gt;sendChat('',`/w "${who}" ${ms.length} removed.`)) .each((m)=&gt;m.remove()); } else { let macroTree = findObjs({type:'macro'}) .reduce((m,mac)=&gt;({ ...m, [mac.get('playerid')]:[ ...(m[mac.get('playerid')]||[]), mac ] }),{}); sendChat('',`/w "${who}" ${f.tree(macroTree)}`); } } }); });
1440557568
Stephen S.
Pro
Marketplace Creator
Sheet Author
API Scripter
+1
1440589424
Natha
KS Backer
Sheet Author
API Scripter
Excellent, thanks
1440592171
The Aaron
Pro
API Scripter
Ha! &nbsp;Some of those bookmarklets I just modified. &nbsp;Nice of you to collect all these here, Vince. =D &nbsp;I've got a small change to that CenterSmalls.js script that I'll edit into your list above. &nbsp;It should really ignore drawings so that I don't screw with all of Stephen's nice scripts. =D
1440599686

Edited 1440599754
Gold
Forum Champion
Nice collection. Very useful. Would like to see some of these rolled-into Roll20 as full time features: Jukebox now-playing highlighting Expand Page Toolbar&nbsp; Center small tokens on grid