Lately I've been playing around with some code-generated drawing and animation. The following is a tutorial describing how I created a tree that sways in the wind. The user generates a small but powerful source of wind by clicking and dragging around the stage. Here is what the final movie looks like:
Drawing the Tree
The tree movie consists of two classes — the main movie or Tree class which generates the tree and handles events, and the Branch class which represents each branch of the tree. Tree.as looks like this:
// Tree.as
package {
import flash.display.*;
import flash.geom.Point;
public class Tree extends MovieClip{
private var trunk:Branch;
public function Tree(){
trunk = new Branch(-90, new Point(stage.stageWidth/2, stage.stageHeight));
addChild(trunk);
}
}
}
At this stage the Tree class is pretty basic. It creates a new branch called trunk at an angle of -90 degrees (straight up), and centers it horizontally at the bottom of the movie.
Branch.as looks like this:
// Branch.as
package {
import flash.display.*;
import flash.geom.Point;
public class Branch extends Shape{
// class vars
private static const baseLength:Number = 100;
// instance vars
private var angle:Number;
private var endPoint:Point;
public function Branch(initialAngle:Number, origin:Point){
angle = initialAngle;
x = origin.x;
y = origin.y;
endPoint = getEndPoint();
drawBranch();
}
private function drawBranch():void{
graphics.clear();
graphics.moveTo(0, 0);
graphics.lineStyle(10, 0x000000);
graphics.lineTo(endPoint.x, endPoint.y);
}
// Using angle and baseLength as polar coordinates get the endPoint of the line as cartesian coordinates.
private function getEndPoint():Point{
return Point.polar(baseLength, degToRad(angle));
}
private static function degToRad(deg:Number):Number{
return deg/57.3;
}
private static function radToDeg(rad:Number):Number{
return rad*57.3;
}
}
}
Branch() takes an initial angle and a point. The point is the branch's origin — where it connects to the rest of the tree or the ground in the case of the trunk. The function drawBranch() uses the Shape class's graphics property to draw out the graphical representation of the branch. This is simply a line drawn from the branches origin to an end point. The endPoint is found using the Point class's polar() method to convert the polar coordinates defined by angle and baseLength, to cartesian coordinates. This is done in getEndPoint(). degToRad() is a utility function that converts a given number of degrees into radians. The sister funciton to degToRad() is radToDeg() which unsurprisingly converts radians to degrees. These are particularly useful functions since some actionScript classes use radians and others use degrees.
Compiling and running Tree will draw a line on the screen.
Not too interesting yet, but this is about to change. The clever thing about what we are about to do is that we won't have to do much to Tree.as to draw the rest of the tree. The Branch class will take care of that with a little bit of recursion. Once each branch is done initializing it will create two more slightly smaller branches at different angles. Updates are in bold.
// Branch.as
package {
import flash.display.*;
import flash.geom.Point;
public class Branch extends Shape{
// class vars
private static const baseLength:Number = 100;
private static const minScale:Number = 0.2;
private static const slope:Number = 0.8;
// instance vars
private var angle:Number;
private var endPoint:Point;
private var globalEndPoint:Point;
private var thisParent:MovieClip
private var children:Array = [];
public function Branch(initialAngle:Number, scale:Number, origin:Point, parentClip:MovieClip){
angle = initialAngle;
x = origin.x;
y = origin.y;
thisParent = parentClip;
scaleX = scaleY = scale;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch();
thisParent.addChild(this);
if(scale*slope > minScale){
sproutBranches(scale*slope);
}
}
private function sproutBranches(scale:Number):void{
// new branch angles are augmented by a random +- 10 degrees
var branchLeft:Branch = new Branch(angle - 30, scale, globalEndPoint, thisParent);
var branchRight:Branch = new Branch(angle + 30, scale, globalEndPoint, thisParent);
children.push(branchLeft);
children.push(branchRight);
}
private function drawBranch():void{
graphics.clear();
graphics.moveTo(0, 0);
graphics.lineStyle(10, 0x000000);
graphics.lineTo(endPoint.x, endPoint.y);
}
// Using angle and baseLength as polar coordinates get the endPoint of the line as cartesian coordinates.
private function getEndPoint():Point{
return Point.polar(baseLength, degToRad(angle));
}
// convert degerees to radians
private static function degToRad(deg:Number):Number{
return deg/57.3;
}
// convert radians to degrees
private static function radToDeg(rad:Number):Number{
return rad*57.3;
}
}
}
One very important thing to take note of is the last few lines of the Branch constructor. Here we call sproutBranches(), a function that attaches two new branches to the current branch. Here we check the current branch's scale against the minScale constant — the minimum allowed size for a branch. If a branch's scale has reached the lower limit for branch size, it will not call sproutBranches(). It will not create any new child branches. If we did not do this check flash runtime would get caught in an infinite loop and would soon crash.
The sproutBranches() function creates two new branches at + and - 30 degrees from the angle of the current branch. They are then added to the children array for later reference.
We've also added globalEndPoint which will be used to keep track of each branch's endPoint relative to the root coordinate plane. This is passed on to child branches to tell them where to set their origins.
We will need to change the parameters passed to trunk in Tree.as. Here we are telling trunk to render at full scale and setting its parent to the root MovieClip. We can also remove the addChild(tree) line since the Branch constructor takes care of adding branches to the stage.
// Tree.as trunk = new Branch(-90, 1, new Point(stage.stageWidth/2, stage.stageHeight), this);
Compile and run this iteration. You should see a more densely populated tree.
That's not bad but it doesn't look very natural. We could improve this by adding a little randomness to the branches. Change lines 37 and 38 of Branch.as to look like this:
// Branch.as var branchLeft:Branch = new Branch(angle - 30 + Math.random()*20 - 10, scale, globalEndPoint, thisParent); var branchRight:Branch = new Branch(angle + 30 + Math.random()*20 - 10, scale, globalEndPoint, thisParent);
Now all of the the branches should be rotated + or - 30 degrees from their parent branches plus and additional random amount somewhere between 10 and -10. This looks more natural:
Generating Wind
I could easily spend all day tweaking the size and shape of this tree but then we'd never get to animate it. Time to move on.
The first thing we're going to do is write some event handlers. One handler will trigger the "generation of wind" when the user clicks somewhere on the stage. Another handler will do the wind generation by moving the branches on ENTER_FRAME events. And finally, another handler will remove the wind generation event handler when the user releases the mouse button.
// Tree.as
package {
import flash.display.*;
import flash.geom.Point;
import flash.events.*;
public class Tree extends MovieClip{
private var trunk:Branch;
public function Tree(){
trunk = new Branch(-90, 1, new Point(stage.stageWidth/2, stage.stageHeight), this);
stage.addEventListener(MouseEvent.MOUSE_DOWN, startWind);
stage.addEventListener(MouseEvent.MOUSE_UP, endWind);
}
private function startWind(e:MouseEvent):void{
addEventListener(Event.ENTER_FRAME, generateWind);
}
private function endWind(e:MouseEvent):void{
removeEventListener(Event.ENTER_FRAME, generateWind);
}
private function generateWind(e:Event):void{
trunk.bend();
}
}
}
The other piece we need to add in this step is the bend() method of the Branch class:
// Branch.as
public function bend():void{
angle = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x));
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch();
}
This method finds the angle of an imaginary line between the mouse and the endPoint of the branch, moves the endPoint so that it lies on that imaginary line, then redraws the branch. It will take a few frames but the branch will end up pointing at the mouse.
Next, we want this method to propagate through the whole tree. This part is a little bit magical. After the bend() function we will reposition each of the branch's children to the the branch's endPoint and then call bend() on each. Remember that each branch has at most only 2 child branches, but since the bend() method is the same for each child branch as it is for the trunk, it will in turn call bend() on the child branches of each of those branches and so on until there are no child branches left. I've broken this task out into a function called positionChildren():
// Branch.as
public function bend():void{
angle = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x)) ;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch();
positionChildren();
}
private function positionChildren():void{
globalEndPoint = localToGlobal(endPoint);
for each(var child:Branch in children){
child.x = globalEndPoint.x;
child.y = globalEndPoint.y;
child.bend();
}
}
Now all the branches point toward the mouse when the user clicks on the stage.
This movement happens a little too quickly. Also, if the mouse is supposed to be the point of wind generation then they should be moving away from it rather than towards it. Let's slow it down and reorient the branches by modifying the bend() function of the Branch class:
// Branch.as
public function bend():void{
var windAngle:Number = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x)) - 180;
if(windAngle - angle < -180){
windAngle += 360;
} else if(windAngle - angle> 360){
windAngle -= 360;
}
if(Math.abs(windAngle - angle) > 1){
angle += (windAngle - angle)/8;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch()
}
positionChildren();
}
First we flip the angle we're working with (the angle between the mouse and the endpoint of the branch) and store it in a variable called windAngle. This will make the branches point away from the mouse rather than toward it. It is the target angle that we want the branch's angle to eventually reach. In the following if() statement we fudge the numbers a bit so that the difference between the target angle and the current angel is always more than -180 and less than 180. This is to prevent erratic behavior at certain angles. For example, if the target angle were 10 and the branch's current angle were -10 we would want the difference to be considered to be 20 degrees (the short way around the circle) rather than -340 degrees (the long way around).
The next step determines how quickly the branch rotates to the target angle. We reset the branch's angle to a number that is somewhere between the current angle and windAngle. We adjust the angle by 1/8 of the difference between angle and windAngle. This is similar to adding a motion tween with an ease out property to a frame in the timeline.
Next lets add resistance. We'll add an another ENTER_FRAME event listener to the Tree class. On every frame the event handler resist() will call a method of the Branch class called returnToRest(). returnToRest() will work in opposition to bend() and attempt to move the branch back to it's original orientation.
// Tree.as
package {
import flash.display.*;
import flash.geom.Point;
import flash.events.*;
public class Tree extends MovieClip{
private var trunk:Branch;
public function Tree(){
trunk = new Branch(-90, 1, new Point(stage.stageWidth/2, stage.stageHeight), this);
stage.addEventListener(MouseEvent.MOUSE_DOWN, startWind);
stage.addEventListener(MouseEvent.MOUSE_UP, endWind);
addEventListener(Event.ENTER_FRAME, resist);
}
private function resist(e:Event):void{
trunk.returnToRest();
}
private function startWind(e:MouseEvent):void{
addEventListener(Event.ENTER_FRAME, generateWind);
}
private function endWind(e:MouseEvent):void{
removeEventListener(Event.ENTER_FRAME, generateWind);
}
private function generateWind(e:Event):void{
trunk.bend();
}
}
}
The first change to the Branch class is to store the angel that the branch starts out at. This is the variable restAngle. Along with angle set it to initialAngle in the constructor.
//Branch.as
public var restAngle:Number;
public function Branch(initialAngle:Number, scale:Number, origin:Point, parentClip:MovieClip){
angle = restAngle = initialAngle;
...
The returnToRest() function works similarly to bend() in that once it is called on trunk it propagates through the entire tree. It uses the same technique that we used to bend the branch away from the mouse, to bend it back to restAngle. returnToRest() makes use of the function unbendChildren() which does essentially the same thing as positionChildren(). It moves child branches to the current branch's endPoint and calls returnToRest() on each of them.
// Branch.as
public function returnToRest():void{
if(Math.abs(restAngle - angle) > 1){
angle += (restAngle - angle)/8;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
// redraw
drawBranch();
}
unbendChildren();
}
private function unbendChildren():void{
globalEndPoint = localToGlobal(endPoint);
for each(var child:Branch in children){
child.x = globalEndPoint.x;
child.y = globalEndPoint.y;
child.returnToRest();
}
}
Compile and run the movie and you should have a tree that bends away from the mouse when you click and drag around the stage.
It seems to me that smaller branches closer to the source of wind should be affected more than those that are further away from it. I created a strength variable in the bend() function that is used to influence how much a branch will bend on each frame. The value of strength is the distance between the mouse and the branch's endPoint. This is found by using the pythagorean theorem (a2 + b2 = c2) on distX and distY.
// Branch.as
public function bend():void{
var windAngle:Number = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x)) - 180;
var distX:Number = mouseX + endPoint.x;
var distY:Number = mouseY + endPoint.y;
var strength:Number = Math.sqrt(distX*distX + distY*distY);
if(windAngle - angle < -180){
windAngle += 360;
} else if(windAngle - angle> 360){
windAngle -= 360;
}
if(Math.abs(windAngle - angle) > 1){
angle += (windAngle - angle)/strength;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch()
}
positionChildren();
}
Additionally, larger branches should not bend as much as smaller ones since they are thicker and less flexible. The variable strength can be truncated based on the branch's scale:
var strength:Number = (Math.sqrt(distX*distX + distY*distY)*scaleX*scaleX)/5;
Multiplying the distance by scaleX twice exaggerates the difference in how much a smaller branch bends compared to how much a larger branch bends. Dividing it all by 5 decreases the overall strength of the wind.
I have one final adjustment before calling it a day. The wind is a little too steady for my taste. Adding some turbulence will make it a little more realistic. Update the generateWind() function of Tree to look like this:
// Tree.as
private function generateWind(e:Event):void{
var turbulence:Number = Math.random()*40 - 20;
trunk.bend(turbulence);
}
turbulence is a random number between -20 and 20. Pass this to bend(). In bend() add turbulence to windAngle:
// Branch.as
public function bend(turbulence:Number):void{
var windAngle:Number = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x)) - 180;
windAngle += turbulence;
...
Don't forget to pass it on to positionChildren() at the end of bend()...
positionChildren(turbulence);
...and to bend() again in positionChildren():
child.bend(turbulence);
The final classes should look like this:
// Tree.as
package {
import flash.display.*;
import flash.geom.Point;
import flash.events.*;
public class Tree extends MovieClip{
private var trunk:Branch;
public function Tree(){
trunk = new Branch(-90, 1, new Point(stage.stageWidth/2, stage.stageHeight), this);
stage.addEventListener(MouseEvent.MOUSE_DOWN, startWind);
stage.addEventListener(MouseEvent.MOUSE_UP, endWind);
addEventListener(Event.ENTER_FRAME, resist);
}
private function resist(e:Event):void{
trunk.returnToRest();
}
private function startWind(e:MouseEvent):void{
addEventListener(Event.ENTER_FRAME, generateWind);
}
private function endWind(e:MouseEvent):void{
removeEventListener(Event.ENTER_FRAME, generateWind);
}
private function generateWind(e:Event):void{
var turbulence:Number = Math.random()*40 - 20;
trunk.bend(turbulence);
}
}
}
// Branch.as
package {
import flash.display.*;
import flash.geom.Point;
public class Branch extends Shape{
// class vars
private static const baseLength:Number = 100;
private static const minScale:Number = 0.2;
private static const slope:Number = 0.8;
// instance vars
private var angle:Number;
private var endPoint:Point;
private var globalEndPoint:Point;
private var thisParent:MovieClip
private var children:Array = [];
public var restAngle:Number;
public function Branch(initialAngle:Number, scale:Number, origin:Point, parentClip:MovieClip){
angle = restAngle = initialAngle;
x = origin.x;
y = origin.y;
thisParent = parentClip;
scaleX = scaleY = scale;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch();
thisParent.addChild(this);
if(scale*slope > minScale){
sproutBranches(scale*slope);
}
}
private function sproutBranches(scale:Number):void{
// new branch angles are augmented by a random +- 10 degrees
var branchLeft:Branch = new Branch(angle - 30 + Math.random()*20 - 10, scale, globalEndPoint, thisParent);
var branchRight:Branch = new Branch(angle + 30 + Math.random()*20 - 10, scale, globalEndPoint, thisParent);
children.push(branchLeft);
children.push(branchRight);
}
public function bend(turbulence:Number):void{
var windAngle:Number = radToDeg(Math.atan2(mouseY + endPoint.y, mouseX + endPoint.x)) - 180;
windAngle += turbulence;
var distX:Number = mouseX + endPoint.x;
var distY:Number = mouseY + endPoint.y;
var strength:Number = (Math.sqrt(distX*distX + distY*distY)*scaleX*scaleX)/5;
if(windAngle - angle < -180){
windAngle += 360;
} else if(windAngle - angle> 360){
windAngle -= 360;
}
if(Math.abs(windAngle - angle) > 1){
angle += (windAngle - angle)/strength;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
drawBranch()
}
positionChildren(turbulence);
}
private function positionChildren(turbulence:Number):void{
globalEndPoint = localToGlobal(endPoint);
for each(var child:Branch in children){
child.x = globalEndPoint.x;
child.y = globalEndPoint.y;
child.bend(turbulence);
}
}
private function drawBranch():void{
graphics.clear();
graphics.moveTo(0, 0);
graphics.lineStyle(10, 0x000000);
graphics.lineTo(endPoint.x, endPoint.y);
}
public function returnToRest():void{
if(Math.abs(restAngle - angle) > 1){
angle += (restAngle - angle)/8;
endPoint = getEndPoint();
globalEndPoint = localToGlobal(endPoint);
// redraw
drawBranch();
}
unbendChildren();
}
private function unbendChildren():void{
globalEndPoint = localToGlobal(endPoint);
for each(var child:Branch in children){
child.x = globalEndPoint.x;
child.y = globalEndPoint.y;
child.returnToRest();
}
}
// Using angle and baseLength as polar coordinates get the endPoint of the line as cartesian coordinates.
private function getEndPoint():Point{
return Point.polar(baseLength, degToRad(angle));
}
// convert degerees to radians
private static function degToRad(deg:Number):Number{
return deg/57.3;
}
// convert radians to degrees
private static function radToDeg(rad:Number):Number{
return rad*57.3;
}
}
}
Now the tree quivers in the wind as well.
Bonus
Here are a few screenshots I took during the development of this movie. These are all unexpected results from testing and debugging. This demonstrates how tweaking one or two variables can result in vastly different patterns. There is much room for exploration here.
This article was originally published on sethmabbott.com.
In 1997, Matt Nadeau, a Vermont native, started his own microbrewery in the basement of his home. He named it Rock Art Brewery, in honor of the ancient petroglyphs that are etched in the rocks of the Vermont mountains surrounding his home. Eventually Matt moved his operation out of his basement and into a larger facility where he produces a full line-up of beers that are distributed throughout Vermont, Massachusetts, Pennsylvania, New Jersey, Connecticut and Arizona. In all regards, Matt had successfully realized his dream and turned a pastime into a profession.
To celebrate Rock Art's tenth anniversary, Matt decided to brew a special beer. Indicative of the ten-year mark, Matt crafted a big, bold beer with a 10% ABV (Alcohol By Volume) and aptly named it "Vermonster. "
Everything was going great for Matt until September 14, 2009. That morning, he received a cease and desist order in the mail from the Hansen Beverage Company, the makers of Monster energy drink, demanding that he:
1. Immediately cease and desist from any distribution, sale or other use of Vermonster in connection with beverages, including the use of any advertising, promotional and point-of-sale-materials that include the infringing mark;
2. Expressly abandon U.S. Trademark Application Serial No. 77/765,863; and
3. Pay to Hansen its attorney's fees incurred in connection with this matter.
Hansen's argument is that , as Monster is planning to enter the alcoholic beverage market, Vermonster will "undoubtedly create a likelihood of confusion and/or dilute the distinctive quality of Hansen's Monster marks. Thus, use of Vermonster infringes Hansen's rights, and constitutes unfair competition under state and federal laws. "
Matt has sought out legal advice from a number of trademark attorneys, who each in turn have stated that should he proceed with litigation, he would most likely be vindicated in the eyes of the law. However, if he were to win in one court, he would be faced with an appeal from Hansen's lawyers in another. And another. And another. Fighting this, he has been advised, will most certainly bankrupt him.
So Matt decided to do what downtrodden, Little Guy's do when bullied by The Man: he resorted to grassroots tactics. However, this isn't your mother's letter-writing campaign.
Matt has smartly gone digital and positioned Rock Art Brewery's website as the hub for his battle against Hansen. The landing page features little other than content relating to his cause and prominently features a large title reading, "ROCK ART BREWERY VS CORPORATE AMERICA. " Just below the battle cry is an embedded, surprisingly well-produced, 6-minute YouTube video detailing his company and the lawsuit facing him. The site includes downloads of Hansen's original cease and desist letter, as well as links to the media coverage Matt has garnered. And, of course, there are the must-have "follow us on " Facebook/Twitter tabs. The #ISupportRockArt hashtag is a popular trending topic (though it has yet to break into the featured top ten). On Facebook, the 1,800-plus fans of the brewery are posting their support, encouraging a boycott of Monster and have even posted the mailing address of Monster's CEO, Rodney C. Saks.
What Rock Art Brewery is attempting to do could very well become a perfect case study of the power of social media as a grassroots tool. Beyond the #ISupportRockArt hashtag, Twitterati have already started posting #monsterboycott, #BoycottMonsterDrinks and #hansenboycott in their tweets. In my two minutes on Twitter observing #ISupportRockArt trending, 25 new tweets were posted featuring the hashtag. Furthermore, this story is only a month old and the exponential rate at which it has gained supporters is illustrative of the wildfire-like nature of the Internet.
Should this campaign gain enough of a following, Hansen will be forced to conduct a cost/benefit analysis of whether or not to pursue with their current course of action. I am willing to bet that had they checked with their PR counsel as well as their legal counsel in the beginning, they probably would've been advised not to have issued the cease and desist in the first place.
Regardless of how this plays out in court, any attempt by Hansen to position Monster in the alcoholic beverage market will inexorably be linked to the Rock Art Brewery name, even when it fades from the Twittersphere, as any future google search for "Monster Energy Drink " or "Hansen Beverage Company " will surely list links to this story amongst its returns. In the end, the move by Hansen to protect its brand from the "likelihood of confusion and/or dilute the distinctive quality of Hansen's Monster marks " will in effect have inadvertently perpetuated that very same scenario to occur.
**UPDATE 10/22/09**
Hansen has withdrawn the C&D order. Here are the details.
Disclaimer:
The Killswitch Collective, LLC is in no way giving legal advice or is supporting or denying the legality of this case, nor are we advocating either side of the issue. We are simply using this scenario as a case study to show how online networks and the digital landscape can quickly and efficiently add momentum to a cause.
Steffan Postaer is the Chairman and Chief Creative Officer of one of the world's largest advertising concerns, Euro RSCG Chicago, where his clients include Barilla, Kraft, Valspar Paint, Beam Spirits and more. Steffan is responsible for their overall creative leadership and quality of the creative product.
A copywriter by trade, he is perhaps best known for his provocative work on behalf of Altoids, The Curiously Strong Mints. Other moments in his career include co-authoring the famous "Not your father's Oldsmobile" campaign for General Motors and penning a commercial for Heinz featuring a teen-aged Matt LaBlanc. He is the recipient of advertising's most prestigious awards including a Kelly Award, Best of Show at the Addys, and a gold and silver Lion from Cannes.
Additionally, Steffan is an avid writer and blogger. His blog, Gods of Advertising, is considered one of the best places on the web for candid, expert commentary on the advertising industry. Steffan has published two novels, including his most recent, The Happy Soul Industry, a book that explores themes of religion and materialism by asking the question, "What if God needed an advertising agency?" Steffan's short stories have been included in the 1994 and 1995 editions of New Voices in Poetry and Prose. His first novel, The Last Generation, was published by the Inkwater Press. Touchstone Television later optioned the story for TV.
Prior to joining Euro RSCG, Steffan was Executive Vice President and Chief Creative Officer of LBWorks (a Leo Burnett company), overseeing creative for their full client roster including Altoids, Handspring, Lexmark and Maytag. Prior to this position, he served as Executive Vice President and Executive Creative Director at Leo Burnett USA, and was a member of Leo Burnett USA's Creative Management Board.
How did you get into writing novels?
I started writing short stories in high school, where I worked for my high school newspaper (I went to Lane Tech, a public school on Western and Addison here in Chicago). At the University of Wisconsin I wrote for both the conservative and super-liberal papers, unbeknownst to either of them. I also wrote for the music newspapers. I took my classes and did reasonably well, but I really spent a lot of my time writing for newspapers and interviewing the bands that came through Madison. My dream was to be a writer for Rolling Stone.
The epiphany I had--I think that this is a fun story--came about from something that all critics are guilty of, which is when you review someone who is terrible and then find clever ways to eviscerate them. As a young, young man I was totally guilty of that. One night, a day or two after I published some rip on a band, I'll never forget what they were called: Whiz Kid. They had tights and long hair and sang 80's rock like Lover Boy and they were terrible. I made fun of them and then this guy in the band came up to me several nights later, he didn't want to fight or anything, he came up to me and let me know that he didn't want to play those songs. He told about how he had a kid and had to pay rent and said that he couldn't play his songs and expect these college kids to come to the show. He let me know that I had really hurt his feelings.
That was bothersome to me. I didn't want to be that guy anymore. At that point I went away from wanting to be a Roger Ebert or a Rolling Stones writer or any of that. Who was I to knock this guy for following his dreams? That's a true story, and I don't think I'm embellishing it. I don't think that I wrote another review after that conversation. That was the change, and that's what led to my becoming a copywriter and an author.
But I do all of it now. When I go home at night, I don't watch TV. Whether it's with writing for work or my blog or my other writing, I'm always writing or trying to either make content or sell the content I've made. That's what interests me now. Until I see my book at the airport in LaGuardia or I'm sitting on the couch next to Oprah Winfrey, I'm not going to stop. That's the way I channel ambition and keep my work ethic going. Writing isn't a chore for me. It can be at first, but it is sort of like a pool. Once you jump in it's chilly but once you're in it and enjoying it, it's the best.
So what compelled you to start blogging?
The first few months I was primarily doing it to learn about blogging and to sort of communicate to my troops without sending memos, which nobody does anymore. So for the first few months I had about 40-50 people, mainly from around the office. Then 70. Then 100. Then I wrote a controversial piece, it got linked and picked up and spiked to 450. Then 700. Now, my record is 1200 in a single day and I average about 400 knuckleheads daily that visit my blog.
Then I launched the Rogues Gallery, my second blog, which is a collection of art and writing from advertising people. It's what we make when we aren't making ads. And so the Rogues Gallery is a place for people to submit anything: their photography, art, poetry, fiction, and essay. It's not a beauty pageant. No prize. There isn't any editorializing. As long as it's not commercial and you are in the industry, I'm going to publish it.
So here you are, an advertising creative that has found this outlet in writing literature that has recently launched a website for other ad creatives to post their non-advertising art. Do you count yourself lucky to have been published and is this your way of providing others with an avenue to get their work noticed?
I had the idea about ten years ago to do a copywriters anthology; I wanted to call it "The Book," because we call our portfolios "books." I couldn't get a publisher interested, for obvious reasons....it's not very commercial or lucrative. We're a small business. Advertising and creative services isn't a big audience. Publishers would say, "Who the hell would buy that?"
So I let this fantasy go dormant. And then as I was blogging I had a minor epiphany, which was "Forget this format, I can do everything I wanted to do in the 90's but couldn't and I can do it all by myself." I didn't need underwriters or anything like that. It's pretty cool. I started out with some artwork by my brother and I worked out all the legal disclaimers.
I just started it and it's doing better then I thought. But it's never enough. When I read about some dumb-ass viral video getting a million hits, I'm thinking about how I think I'm a big deal when I get a thousand. There are a thousand people in that building over there, you know? It's nothing in terms of how many human beings are out there. I mean it isn't really about the numbers, if it was I would've been out of business a while ago.
Everything we've talked about is a passion, a labor of love; the numbers don't necessarily support it. But you do it and you don't worry about things you can't control. I need these outlets. I don't relax well, and I like to be busy doing things that bring me satisfaction.
What part about being a Creative Director do you enjoy most?
Being a Creative Director and critiquing others work....it's not even in my top five things I like about this job. It's like number seven on the list. The "Good Ad, Bad Ad" aspect of the job is the price of entry. Anybody, all my lieutenants, are capable of saying, "That's a good ad. That's a bad ad. Here's how we can make this bad ad better." Everyone that aspires to this job has some kind of talent for that.
What makes this job worthwhile for me is building an esprit de corps, new business pitching, arguing for work, helping people manage accounts and creating momentum for the agency; things which proved critical when I first took this role here. Now we are good at these things and we are trying to be great. The economy has only hindered that, not stopped it.
There are many advertising creatives that had turned novelists: Joseph Heller, Elmore Leonard, F. Scott Fitzgerald, Salman Rusdie, Augusten Burroughs. They share in their writing a common cynicism. Do you think that cynicism is a product of being a Mad Man?
I'm a spiritual person, though I don't define God in the traditional, Judeo-Christian sense. The thing is, in Happy Soul Industry, God isn't searching for an ad agency for cynical reasons, it's cynical because we believe that God shouldn't need an ad agency and that advertising is sleazy. We infer that based on the concept, and that's what I wanted to happen with this book.
Look at the seven deadly sins. Envy, Greed, Lust....all of them, that's what we do! Copywriters merchandise these sins. "I want that! I want to look like her! I want to be like him!" Everything is based on Greed, Envy and Lust. Everything I'm selling for my clients I'm trying to appeal to those sins. We make you want what you don't need.
It's like being in a rock and a hard place, but I don't lose sleep over it, partly because I put it into a story and explore that theme. I think it's a big theme for me personally but it's also an entertaining theme.
There are personal and professional things that happen in your life and create a pattern over time. The pattern for me became a very questioning and introspective look at what I was doing with my life. So I discovered with advertising that I was extolling these sins. Is that the legacy I want to leave behind? The answer? Yes, no, maybe so.
But really, in the book, all characters find redemption. Redemption is a big theme for me, and for all men really. You can't exercise those themes in copywriting; you can only do a really good job writing on behalf of consumer goods and services. But I love advertising! So I love something even though it has these defects.
Where do you stand on the importance of copy vs. art? There are those that believe that rich, well-written copy isn't that effective since the viewer's attention span is so short.
I'm even a victim of having a shorter attention span. I get impatient with my morning newspaper now. I've become so accepting of technology now that getting news from longer formats frustrates me. But with copy, we're just talking about a few sentences. People will continue to read good copy. If they desire what you are trying to sell, if it's feeding them in some way, they will devour what you write.
Advertising is very much popular culture. It's pop culture, but it's culture nonetheless. It defines our society, for better or worse, as much as anything else out there. Advertising is a great common denominator.
This is a difficult time for the advertising industry. How has Euro RCSG managed to persevere through this recession?
I'm a salesman and I'm totally comfortable being a salesman. Maybe not "totally," but more so than my peers, who will talk about anything other than how well their work sold stuff. They'd rather talk about how many awards it won or what their peers think about it, who the director was....anything other than the sales part.
I don't have that problem. I know I can compete against any other agency because of the single fact that I respect the skill set of copywriting as writing words to persuade. My peers try to spin that so that it satisfies their creative imperatives. I have those imperatives, but I have other outlets for that. I recognize that I am a salesman.
With the Altoids "Curiously Strong" campaign, I knew it was good and I knew it would be successful. The best award that came from that is that we established Altoids as the leader in the marketplace. Especially nowadays in a struggling economy, how do you justify winning an award for work that came from a client that went under or is near bankrupt?
Our agency has only been glanced by torpedoes. We haven't taken anything too hard and haven't had any significant lay-offs. We went right through the first year of this recession, I wouldn't say that we have been unscathed because that would be untrue, but relatively strong and secure. That's because I think that we are real about what it is we do.
If you talk to ten people that have my job, I'm pretty certain that, while they wouldn't disagree with what I've said, they wouldn't personally agree with it. They couldn't publically disagree with that because their clients would wonder what they are paying them for. But privately they wouldn't say, "I got into this business to sell cars. I want to make films and shoot pictures." All that vainglorious stuff.
In your creative career, you've seen the emergence of digital communications. I've heard creatives lament that digital is killing advertising creative as we know it. What's your take?
Digital is the latest, greatest screen. But it's still just a screen and we still have to make films that you watch just like the one in your living room. You have to be careful. A lot of clients scream and yell about digital, but they still want the 30-second TV commercial.
I think the best name for a band would be "Skip Intro." Do you want to read this ad or do you want to go to the site? You are going to hit "Skip Intro." Everybody skips the intro to everything. So what? Is that the future? A bunch of annoying ads that get in front of your websites? No. That's junk mail. Banner ads, is that what you mean by digital? Or do you mean branded content? Banner ads are just billboards on the Information Superhighway.
One final question... from The Proust Questionnaire: Who are your favorite characters in fiction?
When I was young, I identified with troubled superheroes, like Spiderman. Of course, that became very vogue later when the movies came out. I sort of identified with those flawed characters.
In college I liked Ayn Rand, there's no question that I was sucked into that. Howard Roark was a favorite character of mine.

