Nonsense Waffle is a word game that I started building as a personal project in February of 2015 using the new real-time Node.js based framework called Meteor.
In this game, players compete by compiling randomly generated words to create funny phrases then try to sell their ideas in the most humorous ways possible, in sort of an advanced like button and popularity challenge with a lot of role-playing elements that’s all based on silly nonsense.
The game is in its infancy right now, but I do have a working demo up and running at nonsense-waffle.meteor.com, with the core functionality to draw word cards and add them together to create what I’m calling a “waffle”. It also has a tab to view those waffles. The admin tab will eventually be only available to game administrators (myself), where I can add new words to the collection or get a JSON backup of the MongoDB collections. If I continue with this project I will be adding user accounts, experience points, searches, challenges, categories, tags, Facebook and g+ integration, animations, a tutorial, chat rooms and much more.
If you open the application on two different devices or tabs, you can witness one of the wonders of the Meteor platform as everything magically updates across the different instances without so much as coding an AJAX call. I can understand why people are saying Meteor is the future of web development.
The first version of this game I built in 2011 using Adobe Flash Builder/Flex with a Python backend running on Google App Engine. You can view a disorganized dump of my workspace from that project in this github repository: github.com/kalinr/Wacky-Words-Facebook-MMO. Back then I was calling this project Wacky Words, but I’m relatively sure now that I’m going to stick with calling it Nonsense Waffle because the game is all about nonsense and waffles are delicious.
Actually, that’s sort of a lie. After renaming this game, I looked up “waffle” and found that the British definition is “to speak or write, especially at great length, without saying anything important or useful.” That is totally the embodiment of this game!
The rough draft of the view page with a few of the waffles I created
In December of 2014 I joined a small team at Smartek21, JavaScripting for Samsung’s Milk Music web player. We used jQuery, Backbone.js, Underscore.js and Require.js, in an MVC structure to communicate with a custom API which interfaced with Slacker’s API to get most of the data. Milk Music is basically Slacker Radio but is Samsung branded and has a better user interface–at least in my opinion.
The application was mostly complete when I began but I helped solve many bugs and added a few minor features that were necessary for production release. We worked with the Samsung team in California through daily conference calls and instant messengers. In February 2015, Samsung decided to move the whole team to California. Since I didn’t want to leave Seattle I had to quit after just two months, despite how much I enjoyed working with this team.
Milk Music web player was launched in March 2015. You can use the player at milk.samsung.com. You will need to sign up for a free Samsung account.
I wanted to get some practice with CSS and Sass and learn CSS animations, so I started fiddling around in a gihub repo. I didn’t set out to create anything specific but wound up making a simple JavaScript animation engine that cycles through a list of objects representing a series of animations, initializing each one on a timer. I made a jQuery and a vanilla JavaScript version. This plunker uses the vanilla version:
Please forgive the ugly nature of this animation. I am not a designer so I decided not to spend time making it look good. I have a lot of experience matching the designs that other people come up with, but designing myself is not my strong-suit.
This engine would work for an animated banner, allowing you to pull in all the important data for the animation in a simple JavaScript object or JSON string. All you would need are your CSS animations.
I made a couple of my own CSS animations, but many of them came from animate.css, though I did change the ‘tada’ animation so that it utilizes Sass preprocessing to handle vendor prefixes a little better. I didn’t bother making this change to all the animations, since I already learned the things I was shooting for.
Frustratingly, Sass does not support interpolation on @import directives. Otherwise I would have set it up to iterate through a list of only the animations we wanted so we could set up all the vendor prefixes for them in one loop. We could have a big Sass file with tons of animations but the final CSS file would only contain the ones we actually want. Instead, I will need to use a preprocessor mixin like Bourbon or Compass if I ever use this in the real world.
In October and November of 2014, I had some time to work on my own personal projects, so I returned to my WordPress plugin Kalin’s PDF Creation Station. You can see the older version in an older post. I have rewritten the client side to use Angular.js and Bootstrap, almost entirely eliminating jQuery from the equation, and added a couple new features such as widget and custom menu support, a new sorting and pagination system for the lists of available posts and generated files, and the ability to generate .txt and .html files.
Here’s a little instructional video I made for the tool page.
You can also see a video for the settings page, which is a different set of PDF features that allows you to put a link on every page to a PDF of that page.
And here’s some screenshots:
1. A portion of the creator tool that creates custom PDF documents from multiple pages and posts. This shows the post list section and some collapsed sections.
2. This is the main section of the multi-create tool showing options and the list of your created files.
3. The main section of the settings page where you set up PDF links that can appear on every page and post.
4. A different shot of the settings page.
5. The configuration box for the widget.
6. A shot of the box that is added to the page/post edit page to specifically control link placement for each page.
From August 2011 to July 2014 I worked for Accretive Technology Group, maintaining their flagship product, a live streaming adult entertainment platform, duplicated over 1500+ websites. I started as a Flash ActionScript developer, maintaining and adding features to their performer client, of which I don’t have any screenshots, as well as the user client shown in this first image.
You can also see the user client by going to Streamate.com in a desktop browser and clicking on any performer. Warning: this is an NSFW site. The screenshots on this page are from the non-nude section.
Within the first year I worked with one other developer to convert our Flash user client into a mobile client for use on the few Android phones that supported Flash. Unfortunately I don’t have one of those phones so I don’t have a screenshot of that either.
Horizontal version of our mobile client shown on a phone.
After about a year I moved over to developing the model portion of the MVC framework for an HTML5 client designed for use on iPad, iPhone and other mobile devices. We focused on iOS because Apple users spend more money, but made sure everything worked on various Android devices as well. We built the app in jQuery and Spine.js, which is an MVC JavaScript framework similar to Backbone.js. I also wrote about 70 unit tests in QUnit and for future maintenance, had to learn a bit of Sass.
Vertical version of our mobile client shown on a phone.
Shortly after we finished this we began converting it for use in a couple different parts of our TV station.
After we finished the TV station, we moved on to building a redesigned Streamate. I took charge of converting the Flash client to the new design, then moved on to random tasks throughout the refactoring of our code.
In the end, I was doing mostly maintenance, bug hunting and adding small features all over the site, my time split between ActionScript, JavaScript, HTML, PHP and CSS. I finally decided to resign because it was getting to the point where I wanted to work on something different and wanted to take a break from development for a few months.
This is a funny word massive multiplayer role playing game built on Flash Builder and Google App Engine. Create funny combinations of words from random choices, then write out a description and try to sell your idea to investors. The more ridiculous the better. Gain cash as you get votes and investors, gain levels and buy skills. My future plans were to be able to buy naughty words, upload videos and images to further illustrate your concept or start rivalries with other players or friends.
I never finished the project and since I stopped working on this project, Google App Engine removed support for Python versions below 2.7, so my app stopped working. It would take a bit of work to migrate this project to 2.7 and if I ever return to this I want to re-do it in AngularJS anyway so I decided to focus on other projects.
Here is an AS2 class that allows you to easily create and animate a slot machine style number scroller with two simple function calls. You can scroll numbers up or down by any increment, stopping and starting on any number. It contains many options using an optional greensock-style vars object. You can fine-tune animation speeds and heights individually for each character, or set a delay or onComplete function. Requires the GreenSock tweening engine.
Here’s an example. Note you can style or move the characters around on the stage virtually any way you’d like.
Download class file and FLA. Note: the SWF will work, but you will need to download greensock for the FLA to publish correctly.
And here is the class itself, comments and everything.
/**
* VERSION: 1.0
* DATE: 2011-01-18
* AS2
* AUTHOR: Kalin Ringkvist
**/
import com.greensock.TweenLite;
import com.greensock.easing.Sine;
import mx.utils.Delegate;
import com.greensock.plugins.TweenPlugin;
import com.greensock.plugins.BlurFilterPlugin;
/**
* NumberScroll is a class designed to conveniently scroll a series of numbers up and down, as in a slot machine or price countdown animation
*
*
* To use this class, first create several movieClips, one for each digit in the number you wish to animate, each containing a textField named 'txt'.
* If you want to scroll from 1 up to 999, or from 200 down to 10, you would place three movieClips on the stage. These movieClips can be identical or
* different from each other, and can contain other content, just as long as they contain a textField named 'txt'. Be sure the textFields also have the
* font properly embedded to show numbers.
*
* Next, create the NumberScroll object, passing in an array containing your movieClips, and an optional vars object to set options.
* Then, call startScroll(), along with the same optional vars object to begin the number scrolling
*
* <b>EXAMPLE:</b><br /><br /><code>
* import com.NumberScroll;
*
* //create the NumberScroll object, passing in an array of movieClips that each contain a textField named 'txt'. In this case, the movieClips are all inside a movieClip called 'numbers'.
* //the second paramater (1), sets the starting value
* var ns = new NumberScroll([numbers.txt1, numbers.txt2, numbers.txt3, numbers.txt4, numbers.txt5], 1);
*
* //start the scrolling. The object parameter is optional, but in this example, we use it to set the end value to 40, delay 2 seconds before beginning, set background textFields to %30 opacity,
* //set delay of .4 of a second between each iteration, call scroll99() upon animation completion, count by 1,
* //set animation height for each individual clip to 80, 80, 60, 40 and 20 pixels, and set the animation duration to 2 seconds, 1 second, 1 second, .8 of a second and .4 of a second
* ns.startScroll({endNum:40, delay:2, backgroundTextAlpha:30, incrementDelay:.4, onComplete:scroll99, increment:1, animHeight:[80, 80, 60, 40, 20], animDuration:[2, 1, 1, .8, .4]});
*
*
*
*</code>
**/
class com.NumberScroll{
private var textBGArray:Array;//the textFields that sit in the background
private var textArray:Array;//first set of moving textfields
private var text2Array:Array;//second set of moving texfields
private var parentMC:MovieClip;//the parent of the movieClips that are passed in
private var startNum:Number;
private var endNum:Number = 0;
private var curNum:Number;
private var curNumString:String;
private var numDigits:Number;//the number of digits in the current number
private var digitSpaces:String = "";//the string we add to the beginning of curNumString to insure it has the proper number of digits
private var delay:Number = 0;//delay before the main animation starts
private var incrementDelay:Number = 200;//delay between each increment, converted to milliseconds
private var increment:Number = -1;//the amount to count by, defaults to decrementing by one
private var backgroundTextAlpha:Number = 0;//the transparency of the background digits
private var animHeight:Array = new Array();//array of heights for the new number animation; must have one item for each textField movieClip
private var animDuration:Array = new Array();//array of animation duration values for each textField moviClip
private var onComplete:Function;//the function to call when the final number is done animating in
private var origYArr:Array;//the Y values for each of our passed in textField movieClips
private var readyToComplete:Boolean = false;//tells the tween complete listener whether we are ready to fire the onComplete function
private var countInt:Number;//the AS2 interval we use to call the animations
private var goingDown:Boolean = true;//whether or not we are incrementing or decrementing
private var arrLength:Number;//the number of textField movieClips in our animation
private var varList = new Array("endNum", "delay", "incrementDelay", "increment", "onComplete", "animHeight", "animDuration", "backgroundTextAlpha");
/**
* Constructor <br /><br />
*
* <b>SPECIAL PROPERTIES</b><br />
* The following special properties may be passed in via the constructor's or startScroll()'s vars parameter, like
* <code>new NumberScroll({endNum:100, onComplete:myFunction, increment:-5, animDuration:[2, 1, .5, .2, .1]})</code>
*
* <ul>
* <li><b> endNum : Number</b> The number you want the animation to end on</li>
*
* <li><b> delay : Number</b> The number of seconds to wait before beginning the animation</li>
*
* <li><b> incrementDelay : Number</b> The delay in seconds between each count iteration</li>
*
* <li><b> increment : Number</b> The amount to add on each increment (use negative numbers to count down)</li>
*
* <li><b> onComplete : Function</b> The function that will be called when the animation completes</li>
*
* <li><b> animHeight : Array</b> An array of numbers, one for each digit, to define the animation height for each individual character</li>
*
* <li><b> animDuration : Array</b> An array of numbers, one for each digit, to define the animation duration for each individual character</li>
*
* <li><b> backgroundTextAlpha : Number</b> Sets the alpha of the digit that sits in the background</li>
*
*
* </ul>
*
* @param vars optionally pass in special properties like
*/
public function NumberScroll(_textArray:Array, _startNum:Number, vars:Object){
TweenPlugin.activate([BlurFilterPlugin]);
textBGArray = _textArray;//setup our new variables
text2Array = new Array();
textArray = new Array();
origYArr = new Array();
parentMC = textBGArray[0]._parent;
startNum = _startNum;
curNum = _startNum;
curNumString = _startNum.toString();
numDigits = textBGArray.length;
var adjustLength:Number = numDigits - curNumString.length;//if we're counting up, see if the start value has fewer digits than the end value
for(var i=0; i < adjustLength; i++){//loop once for every digit difference and add a space each time
digitSpaces = digitSpaces + " ";
}
curNumString = digitSpaces + curNumString;
arrLength = textBGArray.length;
parseVars(vars);//convert any optional passed in vars to be saved till a startScroll() call
for(var i=0; i<arrLength; i++){
duplicateMovieClip(textBGArray[i], "scrollMC_" + i, parentMC.getNextHighestDepth());
textArray[i] = parentMC["scrollMC_" + i];//add the first animation copy to our list
duplicateMovieClip(textBGArray[i], "scrollMC2_" + i, parentMC.getNextHighestDepth());
text2Array[i] = parentMC["scrollMC2_" + i];//add the second animation copy to our list
text2Array[i]._alpha = 0;
textBGArray[i].txt.text = curNumString.substr(i, 1);//set the BG array to correct number
textBGArray[i]._alpha = 0;//we don't actually need this until the animation starts so we hide it in case we don't want it at all
textArray[i].txt.text = curNumString.substr(i, 1);//set the main array to correct number
textArray[i].isCurrent = true;//tells which copy is the one currently visible so we know which to animate out and which to animate in
text2Array[i].isCurrent = false;
origYArr[i] = textArray[i]._y;//record the original Y positions of the numbers
if(animHeight[i] == undefined){//set animation height and duration defaults if we didn't get a proper value passed through the vars object
animHeight[i] = 20;
}
if(animDuration[i] == undefined){
animDuration[i] = .2;
}
}
}
/**
* main function that starts the animation
*
* @param vars object containing any of the optional variables listed for constructor function
*/
public function startScroll(vars:Object){
if(vars.delay || delay){//if we have a delay, delay the beginning of the animation
delay = vars.delay;
TweenLite.delayedCall(delay, Delegate.create(this, startInterval), [vars]);
}else{
startInterval(vars);
}
}
private function parseVars(vars:Object){//loops through our list of allowed vars and sets the ones that have been passed in
for(var i=0; i<varList.length; i++){
if(vars[varList[i]] != undefined){
this[varList[i]] = vars[varList[i]];
}
}
}
/**
* @private
* begins the animation process after the initial delay
*
*/
private function startInterval(vars){
parseVars(vars);
if(endNum > curNum){//determine if we're going up or down
goingDown = false;
}
countInt = setInterval(this, "updateNumber", incrementDelay*1000);//begin the interval
}
/**
* @private
* function that runs once per number increment
*
*/
private function updateNumber(){
curNum += increment;//increment our number
if(curNum <= endNum == goingDown){//if curNum is equal to or moved beyond endNum
curNum = endNum;//make it match
readyToComplete = true;//tell the final tween we're ready to fire the onComplete func
clearInterval(countInt);//and delete the interval so this iteration is our last
}
curNumString = curNum.toString();
matchDigits();
for(var i=0; i<arrLength; i++){//loop once for every textField movieclip
var newMC:MovieClip = text2Array[i];
var curMC:MovieClip = textArray[i];
if(newMC.isCurrent){//figure out which clip is the current one and which is the new one, since they alternate roles
newMC = textArray[i];
curMC = text2Array[i];
}
if(curMC.txt.text != curNumString.substr(i, 1)){
newMC.txt.text = curNumString.substr(i, 1);//set the new clip to the correct text
textBGArray[i].txt.text = curNumString.substr(i, 1);//set background to correct text
textBGArray[i]._alpha = backgroundTextAlpha;
setupClipPair(curMC, newMC, i);//setup starting values and engage animation
}
}
}
/**
* @private
* adjusts curNumString to have the correct number of digits in case number drops a digit, e.g. when it goes from 100 to 99
*
*/
private function matchDigits(){
if(goingDown){
if(curNumString.length + digitSpaces.length < numDigits){
digitSpaces = digitSpaces + " ";
}
}else{//if we're moving up, we may need to strip a space instead of add one
if(curNumString.length + digitSpaces.length > numDigits){
digitSpaces = digitSpaces.substr(0, digitSpaces.length - 1);
}
}
curNumString = digitSpaces + curNumString;
}
/**
* @private
* sets up the pair of animation clips and engages the animation. runs after its been determined which is new and which is current
*
* @param curMC the movieClip that is currently sitting, displaying the current number that is about to be replaced
* @param newMC the movieClip that will be animated in to replace curMC
* @param index the index of these clips in their clip arrays so we can reference matching values for animHeigh, origY, etc
*/
private function setupClipPair(curMC:MovieClip, newMC:MovieClip, index:Number){//checks if it's necessary, then sets up the pair of animation clips and engages the animation. runs after its been determined which is new and which is current
newMC._y = origYArr[index] - animHeight[index];//return new clip to its top, invisible position
newMC._alpha = 0;
TweenLite.to(newMC, 0, {blurFilter:{blurX:10, blurY:50}});//instantly blur so its ready to fade in
//now we actually animate the new number in
TweenLite.to(newMC, animDuration[index], {_alpha:100, _y:origYArr[index], ease:Sine.easeInOut, blurFilter:{blurX:0, blurY:0}, onComplete:Delegate.create(this, tweenComplete)});
//animate the old one out
TweenLite.to(curMC, animDuration[index], {_alpha:0, _y:origYArr[index] + animHeight[index], ease:Sine.easeInOut, blurFilter:{blurX:10, blurY:50}});
curMC.isCurrent = false;
newMC.isCurrent = true;
}
private function tweenComplete(){
if(readyToComplete){
readyToComplete = false;
onComplete();
}
}
}
Here’s a link to a landing page I built with JQuery and CSS for Big Fish Games. Click on the large buttons to select a game to load over the video in the main Flash div. The list of related games at the bottom as well as the ‘try it’ link will update accordingly. I used JQuery’s animation function for the opening drawer, browser scrolling, and the animated glow.
Below is one of the many Flash banner ads I built using the AS2 GreenSock tweening engine that linked to this landing page.
This WordPress plugin allows you to build highly customizable PDF documents from any combination of pages and posts, or add a link to any post to download a PDF of that post. The UI is written in JQuery, the backend uses the open-source PHP library, TCPDF, for PDF generation. For source code, more info or screenshots, visit the PDF Creation Station download page at WordPress.org. You can also search for it in your WordPress admin’s plugin repository.
This image is of the settings page that allows you to customize the PDF and the link that is automatically displayed on every post.
Not pictured is the Tool page, with many similar options, that allows you to compile numerous different pages and posts into a single, highly customized PDF archive. Also not shown is the HTML insert fields and the list of 20 shortcodes you can use to insert headers or other post data.
This WordPress plugin creates a shortcode, PHP snippet, or widget for placing highly customizable lists of posts, pages, or attachments into your post content or theme, built in PHP and JQuery. To look at the source code or get more information, go to the download page on WordPress.org, or search for it in your WordPress admin’s plugin repository.
You can see this plugin in use in the right sidebar of KalinFlash.com. You may notice when rolling over the items in the “Recent Additions” widget, you’ll get the post excerpt as a tooltip, which can’t be done with the regular recent posts widget. The “All Portfolio Entries” is another example of this plugin’s widget feature. There are many options for which posts to show, and you can write your own HTML, CSS, and even JavaScript, totally customizing the display of the lists.
Here’s another example, using the shortcode feature instead of a widget. This is a dropdown list of every portfolio entry on this site, organized alphabetically by title instead of chronologically. You could even get crazy if you wanted to and show the post excerpt whenever an item is selected.
Another example of this plugin exists on every portfolio entry, at the top, where it displays “year”, “technologies” and “involvement”. These three items are custom fields applied to the posts. Technically this isn’t a list. This is an example of Post List set to “none” mode, where all the internal shortcodes refer to the current post instead of the items in the list.
And here’s a screenshot of the main shortcode creation page:
Here’s another example, a CSS based table that displays all posts from the “Flash” category in a random order.