24 entries in 2008

Towards an optimal method of quoting - Kay’s response

Thursday, May 15th, 2008

Hi Dave,

Excellent idea. I like your diagram. Here are my guidelines.

  1. A client will always want to make at least 3-4 changes to everything you show them, bearing that in mind add a further 35%-50% to the estimated time, it will always take longer than you think.
  2. Ask lots of question to define clearly and concisely the objectives of the clients needs, which is not necessarily their wants.
  3. Have the brief, objectives and terms clearly outlined in a written document and what will be delivered at different stages of the project.
  4. Gauge how easy going or difficult a client/job will be, usually the bigger the company the more people are involved in the decision making process.
  5. Get consolidated feedback from one person so you don’t get comments in dribs and drabs. Limit rounds of revisions.
  6. Breakdown stages of the project and invoice after each stage. Do not continue with the next stage until payment has been received. Or else the client has no incentive to pay you!
  7. Clearly state what changes are included in the quote. How many rounds of revisions are included. Are they minor or major ones you may have to charge extra for major changes which are outside of the original scope of the project.
    Renegotiate at this point for more cash to cover the extra work.
  8. Sometimes the client doesn’t really understand the process and what the technical limitations are, so it’s important to make sure that they understand what they are asking for especially when it comes to making lots of changes.
  9. Above all... have fun, keep your cool and professionalism at all times. Make the most out of a difficult situation.

Take care,

Kay

Towards an optimal method of quoting - Bryn’s response

Thursday, May 15th, 2008

I agree with Gee on this it’s always been a problem and it is difficult to see a way round it as there is no one concept of “The Client” that can always be used as a model.

However, what may be a possibility is the establishment of a Fair Trading alliance. Whereby we set up a collaborative committee where all freelancers quote using a set of agreed guidelines that are made publicly available to potential clients. I’m not saying price fixing exactly, more a methodology fixing set of guidelines. Guru.com has some quite good guidelines for producing quotes and it’s well worth reading even if you don’t necessarily agree with it all.

Another consideration is when and when not to introduce the client to the notion of a retainer of some form. There are various retainer models that could be used when it is fairly obvious that the client is going to need repeat work.

Some examples:

  • Fixed Monthly Fee: Fixed monthly hours with a discount on extra hours.
  • Preferred suppliership: Nominal fixed monthly fee that grants a heavy discount on all hours bought. Essentially a subscription to a particular rate card. And a promise that the retaining client gets preference over your time above any of your other clients.

One thing that I’ve always struggled with when quoting is that in most cases the cost of the build is defined by the nature of the design. This makes it really difficult to give a comprehensive quote of design and build upfront. If you provide 3 different options for design then the build costs could vary as much. The best solution to this in my mind is to separate these phases as two distinct orders. So you produce the design for the design fee and this would usually include a variety of routes/options. An estimate for build goes with each of these options but is only an estimate. When given multiple design options most clients tend to treat it as a buffet and choose a bit from each option annoyingly. But as a result a final Build quote can only be accurately provided once the design spec is finalized.

This method also allows the client to process a series of smaller invoices that may not require as much internal approval as one big fat one at the end of a project. It also gives them the option to buy design and source their own build if they wish but maintains a relationship with the designer still.

Another option is packaging. Where we find ourselves creating similar functionality in sites then we may have built ourselves a functional base site that can essentially be re-skinned for the client. In this case the client isn’t buying build they are licensing the use of your content platform and the bespoke part they pay for is the design. If they then require functionality beyond the base then these are charged as add-ons. This is a common structure for people who build using opensource or homemade CMS based sites.

In general I think that designers often make the mistake of seeing themselves and their services as something different than hiring a plumber or a landscape gardener etc. (guilty here). I think it’s important to think about how you would expect to buy these services if you were the client. What would you want to see broken down in the quote etc. The other thing to always make clear to the client is whether they are buying a product or a service. Services on a running clock can tend to scare clients as they can’t perceive the boundaries to costs. Upfront quotes for service can lead to the designer to get screwed by the client in order to keep them happy. The benefit of selling our services as product rather than service means that the client has clearly boundaries of what they are paying for and how much. Then if they want to let the scope creep they can be easily made aware of the incremental costs.

The hardest bit in all of this though is placing value on design. In the client’s mind they can look at the work of two designers and ask why are you charging £400 a day and you only £200 a day when your work seems of a par to me. This is where added value comes in, and not in terms of how many years experience etc. as unless that blatantly manifests itself in the clients’ perception of your solution then it’s bullshit to them. If you are charging yourself out at the higher end then you may need to illustrate that you work quicker due to experience so that implications of change orders are dealt with more efficiently thus reduceing the required num of days etc.

Ok I’ve exhausted myself now and still don’t think I’m any closer to the answer but maybe some of this food for thought.

(oh and don’t forget that there are great docs on the design council site that can be stolen to help justify your costs to clients).

Gee’s response

Thursday, May 15th, 2008

Well I can tell you fella that in the 8 years motion graphics freelance and over a year running a company the issues you describe below are just the same today as they were at the beginning !!

Quoting for a job is never an easy thing - the best thing we do is try and find out what budget a client has and work within that budget and agree with them at the beginning that if we do any extras it'll cost them a daily rate of x

Hope that helps chap!
Gee

Towards an optimal method of quoting

Thursday, May 15th, 2008

I’m now in my 10th year of freelancing (good grief!) but one of the things I still find bloody difficult is the whole quoting / time / cost equation.

I often still (like a twat) am tempted to quote for the “perfect job”, that elusive one in which:

  • The client loves your first designs
  • They provide neat, well-structured, source files
  • Nothing goes wrong in the production phase
  • The few changes there are get approved first time
  • They don’t constantly call you asking you why their email doesn’t work, or how do they view an .swf file, or how to tie their shoelaces
  • The job finishes quickly and on time

(Obviously the joke being that this job just doesn't exist!)

So after getting stung a few times recently with jobs that have gone horribly over, I’m compiling a kind of master list of things to work-in / consider whilst building the quote.

Quotation Criteria

The end result is that I’ll have a proper framework with which to help estimate the real time for a job and most importantly feel confident, not bad, for charging for it. As well the client can be sure of knowing in advance how much things are and not get any nasty shocks when I say “Well, the thing is, it’s going to be £2000 more for this thing I didn't realise / forgot / hoped wouldn't come up, but it has”.

Obviously it will be different depending on who provides the job (I’m learning to spot problem clients earlier now), and what the industry is so it’s not a “one size fits all” solution, but will attempt to cover as many bases as possible.

So, I'd like to invite people to pitch in now with their thoughts reply with a list of things that have affected you over the years, and any techniques you now use, and in conjunction with all my other designer pals, I will try to consolidate everyone's input, and will be able to offer something back when done.

Looking forward to your comments,
Cheers,
Dave

Particle Visualizer and Papervision3D Exporter

A script to visualize particle motion and export particle and camera data to Flash's Papervision 3D

Saturday, May 10th, 2008

Papervision 3D is the latest in 3D development for the Adobe Flash platform. It supports a variety of methods for creating 3D content, from basic primitives, to import via the Collada file format. However, being XML-based, Collada files are pretty huge - getting a 300 frame, 150 particle animation into Flash was creating files that were about 22MB!

After trying various approaches to get animation data from 3dsmax to Flash, Good Technology got in contact with me to write an exporter in Max Script for their latest Papervision job for the Audi RS6 microsite.

The plugin has 2 main functions:

  • Visualize particle motion before export
  • Export particle and camera data from max's Z-up coordinate system to Papervision's Y-up coordinate system

More information and screengrabs are available on my portfolio site.

Screenshot

Interface

As with all interfaces I write for 3dsmax, I do my best to provide as much feedback as possible. Note:

  • Group titles are updated with the latest information
  • Dropdowns have an update button
  • Tasks have multiple customization options
  • Progress bars and dialog preferences come as standard

Download

This plugin is not available for download, however please contact me if you wish to discuss custom Papervision3D or any other development.

Time Stamper

A struct to make light work of timing tasks, benchmarking, etc

Friday, May 9th, 2008

This struct basically makes timing things much easier by packaging a few useful methods together.

Now, instead of writing and retrieving timestamp variables, and formatting strings to the listener, you simply call struct methods such as start(), end(), and print(), or see a report of the process with getReport().

Example Code

Create a new Time Stamper, assigning a task name:

ts = timeStamper "Testing"

Time something, and alert the results in a messagebox:

ts.start()
-- your code here
ts.alert()

Benchmark a series of tests, and print the results to the listener when completed:

for i = 1 to 10 do (
	ts.start()
	-- your code here
	ts.end()
)
ts.print average:true
-- Average processing time for 'Testing' was 24.6162 seconds, based on 10 timed sessions.

Methods

Starting and stopping

  • <number> start() - start timing. Returns start time
  • <number> end() - end timing. Returns last timing duration
  • <void> reset() - reset all timing data

Getting results in English

  • <void> print average: <boolean> difference: <TimeStamper> - end timing and print results to listener
  • <void> prompt average: <boolean> difference: <TimeStamper> - end timing and prompt results to notification area
  • <void> alert average: <boolean> difference: <TimeStamper> - end timing and alert the results in a messagebox

For the above methods, the following keyword arguments can be supplied:

  • average <boolean> - returns the average result of all the timed tests since the TimeStamper was instantiated or last reset
  • difference <TimeStamper> - returns the comparative difference between another TimeStamper, in English, e.g."Test 1' was 2.58 times quicker than 'Test 2'"

Getting results as numbers

  • <number> getLast() - gets the last single timed session (alias for duration property)
  • <number> getTotal() - gets the total of all timed sessions
  • <number> getAverage() - gets the average of all timed sessions
  • <array> getDifference difference: <TimeStamper> average: <boolean> - Returns a 3-element array representing how much quicker one time stamper is than another.

The array's elements are ordered like so:

  1. <number> - how many times quicker the quickest Time Stamper was
  2. <string> - the task name of the quicker time stamper
  3. <string> - the task name of the slower time stamper

Getting results as an Excel-compatible report

  • <string> getReport columns:<array> step:<number> output:<string/name/windowstream/filestream> - gets all timed sessions as a customizable report

The step property defines how many iterations to average/total values for, and defaults to 10.

The columns property can take any of the following name values:

  • #index - the numeric index of the row, e.g. 1, 2, 3
  • #step - the current step, e.g. 1, 11, 21
  • #stepaverage - the average of all measurements from the current step
  • #steptotal - the total of all the measurements from the current step
  • #slower - how much slower the current step was than the fastest step
  • #quicker - how much quicker the current step was than the slowest step
  • #total - the cumulative total from all measurements so far

The output property defines where the generate report will be output to. Values can be:

  • No value - the report will be returned as a string
  • "path/to/file.txt" - a file path. If the file exists it will be overwritten, if not it will be created
  • #window - a new script window
  • <windowstream> - a reference to a windowstream
  • <stringstream> - a reference to a stringstream

Download a report that was built using calls to getReport() in Excel 2007 or in Excel 2003

Properties

  • duration <number> - the duration of the last timed session
  • durations <array> - an array of all timed sessions
  • task <string> - the name of the current timed task

Demo and case study

Check out a case study and download a demo script here:

Download

Download TimeStamper.ms.

Progress Bar Updater

A struct to simply and easily update a progress bar by providing just start and end values, and calling update()

Thursday, May 1st, 2008

Updating a progress bar is fairly simple in conceptual terms, but it always takes a slightly different bit of code to do it right:

  • What if the sequence is backwards?
  • What if you're incrementing in units of 2, or 3, or 10?
  • How big is the progress jump?

It makes sense to abstract the process and stick it in a struct. The progressBarUpdater does just that, and really is as simple as:

  1. Create a struct instance
  2. Initialize it with a reference to a progress bar, and the known start and end values of the calculation
  3. Call update() for each loop iteration.

In code, starting with a 2-line initialization, it looks like this:

pbu = progressBarUpdater()
pbu.init pb1 startValue endValue

And to update the progress bar, this is the only command you need to place in the loop:

pbu.update()

The pbUpdater struct already knows everything it needs to know, so it's just one call.

Download

Download progressBarUpdater.ms.

UI Manager

Save and load rollout settings, such as size, position, control states, items, etc to a preferences file

Thursday, May 1st, 2008

A struct containing methods to save and load rollout settings, such as size, position, control states, items, etc
to a preferences file, as well as methods to accomplish UI control tasks for example keeping one spinner higher or lower
than another when editing ranges.

Usage

To manage a rollout or dialog, you simply:

  1. create a struct instance
  2. initialize it with a reference to a rollout
  3. Manage the rollout by calling the struct's methods

In code, that's as easy as this:

ui = uiManager()
ui.initialize roTools
ui.loadPosition()

All settings are saved to an .ini file named after the rollout title, so in this case, an ini file named "Tools.ini" would be saved to your max installation's /plugcfg folder.

Methods

This is a work in progress, and will be updated from time to time with new methods.

Dialog functions

  • savePosition - save the dialog position
  • loadPosition - load the dialog position
  • saveSize - save the dialog size
  • loadSize - restore the dialog size

Rollout functions

  • autoSizeFloater - resize the floater to exactly the size of all open rollouts
  • toggleRollout - set one or several rollouts open
  • loadRolloutStates - save all rolled-up / down states (not yet implemented)
  • saveRolloutStates - load all rolled-up / down states (not yet implemented)

Control functions

  • updateSpinnerPair - ensure that spinners react as a pair, eg one can never go higher than the other
  • moveListboxItem - move selected Listbox items up or down, and have them remain selected
  • moveMultiListboxItems - move multiple MultiListbox selections up or down, and have them remain selected
  • getValue - abstract function to get a control's value (not yet implimented)
  • setValue - abstract function to set a controls's value (not yet implimented)

Preference functions (WIP)

  • saveValue - save a control's value
  • loadValue - load a control's value
  • saveAllValues - save all controls' values
  • loadAllValues - load all controls' values

Downloads

Download uiManager0.52.ms. Place in your scripts/startup directory, or just include the file when you need it.

These example files will get you started:

Flash “PHPLoadVars” class

Natively send complex / multidimensional variables from Flash to PHP with this extended LoadVars class

Thursday, February 28th, 2008

I was pitching in on a forum thread Send an object to a php script the other day and some chap wanted to send complex objects to PHP using LoadVars. "Can't be done!" was the reply.

Oh yeah? Well if PHP can deserialize array variables from HTML forms, I don't see why we can't do the same with Flash.

Well it took about 15 minutes to work up a rough proof-of-concept that worked for GETs only, but it got me thinking to do something nice with:

  • a proper class
  • that works with POST (so you can send BIG objects!)
  • and has a recursive serialization routine (so you just give it any nested data-structure, and it chews through all of it)

PHPLoadVars (also works with Ruby)

Well it couldn't be simpler, really. Just create a new PHPLoadVars object, ask it to serialize your data, and send:

var data :Object      = {data:[1,2,{sub_object:{name:'Dave',age:33,gender:'male'}},3,4,5]}
var lv	 :LoadVars    = new PHPLoadVars();
lv.serialize(data)
lv.send('process.php')

Then PHP receives the complex variables natively like so (a quick print_r($_POST); shows the results):

Array
(
    [data] => Array
        (
            [0] => 1
            [1] => 2
            [2] => Array
                (
                    [sub_object] => Array
                        (
                            [gender] => male
                            [age] => 33
                            [name] => Dave
                        )

                )

            [3] => 3
            [4] => 4
            [5] => 5
        )

)

Just to be absolutely clear - this result isn't manually de-serialized, or run through any processing routines - PHP automatically reads in data serialized using square brackets (which is how things were serialized in Flash) as numeric or associative arrays, so your data is literally ready to go as soon as the headers have been processed!

And you can make your data-structure as deep as you like...

Could it be any easier!? No. It couldn't! Not even XML is this easy!

Download

Download the class file, sample .fla, and a basic PHP file to echo the variables to screen.

LoadPHPVars.zip

jQuery Favelet

Add jQuery functionality to any browsable web page

Saturday, February 23rd, 2008

Any developer worth his salt knows about Firebug and its JavaScript console. I use it all the time to tinker with web pages, test stuff out and debug my own pages.

But sometimes, if I'm *cough* hacking *cough cough* someone else's page, I need the added power of jQuery! But what if the page doesn't have it?

Simple - load jQuery from a remote source manually through a javascript: call.

Save Favelet

Just drag the link below to your Links toolbar, then click it on any page to jQuery-enable it.

Add jQuery to page

Test it out!

Save the Favelet and click on it (or just click the link above), then: animate page.

Simple!